summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt421
-rw-r--r--src/network/access/access.pri124
-rw-r--r--src/network/access/http2/bitstreams.cpp42
-rw-r--r--src/network/access/http2/bitstreams_p.h44
-rw-r--r--src/network/access/http2/hpack.cpp92
-rw-r--r--src/network/access/http2/hpack_p.h43
-rw-r--r--src/network/access/http2/hpacktable.cpp57
-rw-r--r--src/network/access/http2/hpacktable_p.h44
-rw-r--r--src/network/access/http2/http2.pri17
-rw-r--r--src/network/access/http2/http2frames.cpp58
-rw-r--r--src/network/access/http2/http2frames_p.h59
-rw-r--r--src/network/access/http2/http2protocol.cpp141
-rw-r--r--src/network/access/http2/http2protocol_p.h55
-rw-r--r--src/network/access/http2/http2streams.cpp40
-rw-r--r--src/network/access/http2/http2streams_p.h40
-rw-r--r--src/network/access/http2/huffman.cpp53
-rw-r--r--src/network/access/http2/huffman_p.h46
-rw-r--r--src/network/access/qabstractnetworkcache.cpp104
-rw-r--r--src/network/access/qabstractnetworkcache.h46
-rw-r--r--src/network/access/qabstractnetworkcache_p.h40
-rw-r--r--src/network/access/qabstractprotocolhandler.cpp40
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h44
-rw-r--r--src/network/access/qdecompresshelper.cpp781
-rw-r--r--src/network/access/qdecompresshelper_p.h108
-rw-r--r--src/network/access/qftp.cpp2470
-rw-r--r--src/network/access/qftp_p.h176
-rw-r--r--src/network/access/qhsts.cpp117
-rw-r--r--src/network/access/qhsts_p.h60
-rw-r--r--src/network/access/qhstspolicy.cpp65
-rw-r--r--src/network/access/qhstspolicy.h55
-rw-r--r--src/network/access/qhstsstore.cpp54
-rw-r--r--src/network/access/qhstsstore_p.h46
-rw-r--r--src/network/access/qhttp1configuration.cpp154
-rw-r--r--src/network/access/qhttp1configuration.h62
-rw-r--r--src/network/access/qhttp2configuration.cpp82
-rw-r--r--src/network/access/qhttp2configuration.h60
-rw-r--r--src/network/access/qhttp2connection.cpp1752
-rw-r--r--src/network/access/qhttp2connection_p.h372
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp492
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h46
-rw-r--r--src/network/access/qhttpheaderparser.cpp221
-rw-r--r--src/network/access/qhttpheaderparser_p.h101
-rw-r--r--src/network/access/qhttpheaders.cpp1551
-rw-r--r--src/network/access/qhttpheaders.h282
-rw-r--r--src/network/access/qhttpheadershelper.cpp25
-rw-r--r--src/network/access/qhttpheadershelper_p.h30
-rw-r--r--src/network/access/qhttpmultipart.cpp130
-rw-r--r--src/network/access/qhttpmultipart.h48
-rw-r--r--src/network/access/qhttpmultipart_p.h49
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp649
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h119
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp444
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h62
-rw-r--r--src/network/access/qhttpnetworkheader.cpp90
-rw-r--r--src/network/access/qhttpnetworkheader_p.h60
-rw-r--r--src/network/access/qhttpnetworkreply.cpp429
-rw-r--r--src/network/access/qhttpnetworkreply_p.h121
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp106
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h63
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp119
-rw-r--r--src/network/access/qhttpprotocolhandler_p.h46
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp177
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h84
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp78
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h45
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp844
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h263
-rw-r--r--src/network/access/qnetworkaccesscache.cpp317
-rw-r--r--src/network/access/qnetworkaccesscache_p.h53
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp101
-rw-r--r--src/network/access/qnetworkaccesscachebackend_p.h50
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp143
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h48
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp146
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h48
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp440
-rw-r--r--src/network/access/qnetworkaccessftpbackend_p.h126
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp1091
-rw-r--r--src/network/access/qnetworkaccessmanager.h116
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h125
-rw-r--r--src/network/access/qnetworkcookie.cpp245
-rw-r--r--src/network/access/qnetworkcookie.h57
-rw-r--r--src/network/access/qnetworkcookie_p.h56
-rw-r--r--src/network/access/qnetworkcookiejar.cpp127
-rw-r--r--src/network/access/qnetworkcookiejar.h40
-rw-r--r--src/network/access/qnetworkcookiejar_p.h40
-rw-r--r--src/network/access/qnetworkdiskcache.cpp268
-rw-r--r--src/network/access/qnetworkdiskcache.h40
-rw-r--r--src/network/access/qnetworkdiskcache_p.h57
-rw-r--r--src/network/access/qnetworkfile.cpp49
-rw-r--r--src/network/access/qnetworkfile_p.h42
-rw-r--r--src/network/access/qnetworkreply.cpp159
-rw-r--r--src/network/access/qnetworkreply.h57
-rw-r--r--src/network/access/qnetworkreply_p.h42
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp49
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h40
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp85
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h54
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp1046
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h88
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp452
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h82
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp595
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h95
-rw-r--r--src/network/access/qnetworkrequest.cpp985
-rw-r--r--src/network/access/qnetworkrequest.h118
-rw-r--r--src/network/access/qnetworkrequest_p.h84
-rw-r--r--src/network/access/qnetworkrequestfactory.cpp721
-rw-r--r--src/network/access/qnetworkrequestfactory.h100
-rw-r--r--src/network/access/qnetworkrequestfactory_p.h56
-rw-r--r--src/network/access/qrestaccessmanager.cpp828
-rw-r--r--src/network/access/qrestaccessmanager.h127
-rw-r--r--src/network/access/qrestaccessmanager_p.h91
-rw-r--r--src/network/access/qrestreply.cpp608
-rw-r--r--src/network/access/qrestreply.h71
-rw-r--r--src/network/access/qrestreply_p.h40
-rw-r--r--src/network/access/qsocketabstraction_p.h91
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp1304
-rw-r--r--src/network/access/qspdyprotocolhandler_p.h232
-rw-r--r--src/network/android/jar/.gitignore6
-rw-r--r--src/network/android/jar/CMakeLists.txt19
-rw-r--r--src/network/android/jar/build.gradle51
-rw-r--r--src/network/android/jar/settings.gradle1
-rw-r--r--src/network/android/jar/src/org/qtproject/qt/android/network/QtNetwork.java64
-rw-r--r--src/network/bearer/bearer.pri19
-rw-r--r--src/network/bearer/qbearerengine.cpp101
-rw-r--r--src/network/bearer/qbearerengine_p.h115
-rw-r--r--src/network/bearer/qbearerplugin.cpp57
-rw-r--r--src/network/bearer/qbearerplugin_p.h81
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp386
-rw-r--r--src/network/bearer/qnetworkconfigmanager.h100
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.cpp517
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.h141
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp593
-rw-r--r--src/network/bearer/qnetworkconfiguration.h138
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h99
-rw-r--r--src/network/bearer/qnetworksession.cpp740
-rw-r--r--src/network/bearer/qnetworksession.h150
-rw-r--r--src/network/bearer/qnetworksession_p.h157
-rw-r--r--src/network/bearer/qsharednetworksession.cpp102
-rw-r--r--src/network/bearer/qsharednetworksession_p.h92
-rw-r--r--src/network/compat/removed_api.cpp70
-rw-r--r--src/network/configure.cmake386
-rw-r--r--src/network/configure.json493
-rw-r--r--src/network/configure.pri13
-rw-r--r--src/network/doc/images/network-examples.pngbin8946 -> 0 bytes
-rw-r--r--src/network/doc/images/network-examples.webpbin0 -> 8250 bytes
-rw-r--r--src/network/doc/qtnetwork.qdocconf18
-rw-r--r--src/network/doc/snippets/CMakeLists.txt15
-rw-r--r--src/network/doc/snippets/code/doc_src_qtnetwork.cpp53
-rw-r--r--src/network/doc/snippets/code/src_network_access_qftp.cpp109
-rw-r--r--src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp40
-rw-r--r--src/network/doc/snippets/code/src_network_access_qhttppart.cpp40
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp71
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp60
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkreply.cpp55
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkrequest.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp27
-rw-r--r--src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp104
-rw-r--r--src/network/doc/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp58
-rw-r--r--src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp60
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp54
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qhostaddress.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp63
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp40
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkproxy.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qabstractsocket.cpp65
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qlocalsocket_unix.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qnativesocketengine.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qtcpserver.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qdtls.cpp54
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qdtlscookie.cpp51
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslcertificate.cpp57
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp53
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp40
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp65
-rw-r--r--src/network/doc/snippets/network/CMakeLists.txt8
-rw-r--r--src/network/doc/snippets/network/network.pro (renamed from src/network/doc/snippets/code/doc_src_qtnetwork.pro)0
-rw-r--r--src/network/doc/snippets/network/tcpwait.cpp61
-rw-r--r--src/network/doc/src/bearermanagement.qdoc240
-rw-r--r--src/network/doc/src/dontdocument.qdoc28
-rw-r--r--src/network/doc/src/examples.qdoc37
-rw-r--r--src/network/doc/src/external-resources.qdoc28
-rw-r--r--src/network/doc/src/network-programming.qdoc60
-rw-r--r--src/network/doc/src/qt6-changes.qdoc185
-rw-r--r--src/network/doc/src/qtnetwork.qdoc82
-rw-r--r--src/network/doc/src/ssl.qdoc91
-rw-r--r--src/network/kernel/kernel.pri95
-rw-r--r--src/network/kernel/qauthenticator.cpp471
-rw-r--r--src/network/kernel/qauthenticator.h41
-rw-r--r--src/network/kernel/qauthenticator_p.h58
-rw-r--r--src/network/kernel/qdnslookup.cpp838
-rw-r--r--src/network/kernel/qdnslookup.h185
-rw-r--r--src/network/kernel/qdnslookup_android.cpp56
-rw-r--r--src/network/kernel/qdnslookup_dummy.cpp15
-rw-r--r--src/network/kernel/qdnslookup_p.h251
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp639
-rw-r--r--src/network/kernel/qdnslookup_win.cpp274
-rw-r--r--src/network/kernel/qdnslookup_winrt.cpp157
-rw-r--r--src/network/kernel/qhostaddress.cpp293
-rw-r--r--src/network/kernel/qhostaddress.h92
-rw-r--r--src/network/kernel/qhostaddress_p.h42
-rw-r--r--src/network/kernel/qhostinfo.cpp251
-rw-r--r--src/network/kernel/qhostinfo.h119
-rw-r--r--src/network/kernel/qhostinfo_p.h83
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp266
-rw-r--r--src/network/kernel/qhostinfo_win.cpp40
-rw-r--r--src/network/kernel/qnetconmonitor_darwin.mm181
-rw-r--r--src/network/kernel/qnetconmonitor_p.h83
-rw-r--r--src/network/kernel/qnetconmonitor_stub.cpp89
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp403
-rw-r--r--src/network/kernel/qnetworkdatagram.cpp44
-rw-r--r--src/network/kernel/qnetworkdatagram.h46
-rw-r--r--src/network/kernel/qnetworkdatagram_p.h40
-rw-r--r--src/network/kernel/qnetworkinformation.cpp774
-rw-r--r--src/network/kernel/qnetworkinformation.h96
-rw-r--r--src/network/kernel/qnetworkinformation_p.h156
-rw-r--r--src/network/kernel/qnetworkinterface.cpp87
-rw-r--r--src/network/kernel/qnetworkinterface.h55
-rw-r--r--src/network/kernel/qnetworkinterface_linux.cpp66
-rw-r--r--src/network/kernel/qnetworkinterface_p.h56
-rw-r--r--src/network/kernel/qnetworkinterface_uikit_p.h61
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp162
-rw-r--r--src/network/kernel/qnetworkinterface_unix_p.h42
-rw-r--r--src/network/kernel/qnetworkinterface_win.cpp42
-rw-r--r--src/network/kernel/qnetworkinterface_winrt.cpp245
-rw-r--r--src/network/kernel/qnetworkproxy.cpp224
-rw-r--r--src/network/kernel/qnetworkproxy.h72
-rw-r--r--src/network/kernel/qnetworkproxy_android.cpp85
-rw-r--r--src/network/kernel/qnetworkproxy_darwin.cpp (renamed from src/network/kernel/qnetworkproxy_mac.cpp)196
-rw-r--r--src/network/kernel/qnetworkproxy_generic.cpp84
-rw-r--r--src/network/kernel/qnetworkproxy_libproxy.cpp62
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp252
-rw-r--r--src/network/kernel/qtldurl.cpp215
-rw-r--r--src/network/kernel/qtldurl_p.h33
-rw-r--r--src/network/kernel/qtnetworkglobal.h55
-rw-r--r--src/network/kernel/qtnetworkglobal_p.h51
-rw-r--r--src/network/kernel/qurlinfo.cpp727
-rw-r--r--src/network/kernel/qurlinfo_p.h133
-rw-r--r--src/network/network.pro43
-rw-r--r--src/network/qt_cmdline.cmake12
-rw-r--r--src/network/socket/qabstractsocket.cpp417
-rw-r--r--src/network/socket/qabstractsocket.h87
-rw-r--r--src/network/socket/qabstractsocket_p.h89
-rw-r--r--src/network/socket/qabstractsocketengine.cpp48
-rw-r--r--src/network/socket/qabstractsocketengine_p.h61
-rw-r--r--src/network/socket/qhttpsocketengine.cpp184
-rw-r--r--src/network/socket/qhttpsocketengine_p.h61
-rw-r--r--src/network/socket/qlocalserver.cpp127
-rw-r--r--src/network/socket/qlocalserver.h55
-rw-r--r--src/network/socket/qlocalserver_p.h54
-rw-r--r--src/network/socket/qlocalserver_tcp.cpp56
-rw-r--r--src/network/socket/qlocalserver_unix.cpp182
-rw-r--r--src/network/socket/qlocalserver_win.cpp117
-rw-r--r--src/network/socket/qlocalsocket.cpp134
-rw-r--r--src/network/socket/qlocalsocket.h69
-rw-r--r--src/network/socket/qlocalsocket_p.h75
-rw-r--r--src/network/socket/qlocalsocket_tcp.cpp93
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp258
-rw-r--r--src/network/socket/qlocalsocket_win.cpp402
-rw-r--r--src/network/socket/qnativesocketengine.cpp114
-rw-r--r--src/network/socket/qnativesocketengine_p.h266
-rw-r--r--src/network/socket/qnativesocketengine_p_p.h189
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp181
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp187
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp1815
-rw-r--r--src/network/socket/qnativesocketengine_winrt_p.h243
-rw-r--r--src/network/socket/qnet_unix_p.h51
-rw-r--r--src/network/socket/qsctpserver.cpp42
-rw-r--r--src/network/socket/qsctpserver.h42
-rw-r--r--src/network/socket/qsctpserver_p.h40
-rw-r--r--src/network/socket/qsctpsocket.cpp49
-rw-r--r--src/network/socket/qsctpsocket.h42
-rw-r--r--src/network/socket/qsctpsocket_p.h46
-rw-r--r--src/network/socket/qsocks5socketengine.cpp239
-rw-r--r--src/network/socket/qsocks5socketengine_p.h69
-rw-r--r--src/network/socket/qtcpserver.cpp133
-rw-r--r--src/network/socket/qtcpserver.h44
-rw-r--r--src/network/socket/qtcpserver_p.h44
-rw-r--r--src/network/socket/qtcpsocket.cpp48
-rw-r--r--src/network/socket/qtcpsocket.h50
-rw-r--r--src/network/socket/qtcpsocket_p.h40
-rw-r--r--src/network/socket/qudpsocket.cpp50
-rw-r--r--src/network/socket/qudpsocket.h49
-rw-r--r--src/network/socket/socket.pri91
-rw-r--r--src/network/ssl/qasn1element.cpp379
-rw-r--r--src/network/ssl/qasn1element_p.h188
-rw-r--r--src/network/ssl/qdtls.cpp476
-rw-r--r--src/network/ssl/qdtls.h52
-rw-r--r--src/network/ssl/qdtls_openssl.cpp1401
-rw-r--r--src/network/ssl/qdtls_openssl_p.h212
-rw-r--r--src/network/ssl/qdtls_p.h132
-rw-r--r--src/network/ssl/qocsp_p.h40
-rw-r--r--src/network/ssl/qocspresponse.cpp83
-rw-r--r--src/network/ssl/qocspresponse.h64
-rw-r--r--src/network/ssl/qocspresponse_p.h41
-rw-r--r--src/network/ssl/qpassworddigestor.cpp144
-rw-r--r--src/network/ssl/qpassworddigestor.h44
-rw-r--r--src/network/ssl/qssl.cpp181
-rw-r--r--src/network/ssl/qssl.h150
-rw-r--r--src/network/ssl/qssl_p.h53
-rw-r--r--src/network/ssl/qsslcertificate.cpp514
-rw-r--r--src/network/ssl/qsslcertificate.h86
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp763
-rw-r--r--src/network/ssl/qsslcertificate_p.h151
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp551
-rw-r--r--src/network/ssl/qsslcertificate_schannel.cpp62
-rw-r--r--src/network/ssl/qsslcertificate_winrt.cpp113
-rw-r--r--src/network/ssl/qsslcertificateextension.cpp40
-rw-r--r--src/network/ssl/qsslcertificateextension.h42
-rw-r--r--src/network/ssl/qsslcertificateextension_p.h40
-rw-r--r--src/network/ssl/qsslcipher.cpp47
-rw-r--r--src/network/ssl/qsslcipher.h49
-rw-r--r--src/network/ssl/qsslcipher_p.h40
-rw-r--r--src/network/ssl/qsslconfiguration.cpp241
-rw-r--r--src/network/ssl/qsslconfiguration.h81
-rw-r--r--src/network/ssl/qsslconfiguration_p.h54
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp736
-rw-r--r--src/network/ssl/qsslcontext_openssl_p.h131
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp101
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h85
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_dummy.cpp57
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_openssl.cpp186
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_p.h53
-rw-r--r--src/network/ssl/qsslellipticcurve.cpp110
-rw-r--r--src/network/ssl/qsslellipticcurve.h71
-rw-r--r--src/network/ssl/qsslellipticcurve_dummy.cpp71
-rw-r--r--src/network/ssl/qsslellipticcurve_openssl.cpp177
-rw-r--r--src/network/ssl/qsslerror.cpp58
-rw-r--r--src/network/ssl/qsslerror.h56
-rw-r--r--src/network/ssl/qsslkey.h50
-rw-r--r--src/network/ssl/qsslkey_mac.cpp99
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp383
-rw-r--r--src/network/ssl/qsslkey_p.cpp352
-rw-r--r--src/network/ssl/qsslkey_p.h119
-rw-r--r--src/network/ssl/qsslkey_qt.cpp789
-rw-r--r--src/network/ssl/qsslkey_schannel.cpp178
-rw-r--r--src/network/ssl/qsslkey_winrt.cpp169
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.cpp80
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h63
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator_p.h40
-rw-r--r--src/network/ssl/qsslserver.cpp412
-rw-r--r--src/network/ssl/qsslserver.h61
-rw-r--r--src/network/ssl/qsslserver_p.h71
-rw-r--r--src/network/ssl/qsslsocket.cpp1316
-rw-r--r--src/network/ssl/qsslsocket.h108
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp1510
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h137
-rw-r--r--src/network/ssl/qsslsocket_mac_shared.cpp155
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp2131
-rw-r--r--src/network/ssl/qsslsocket_openssl_android.cpp88
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h181
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp1275
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h724
-rw-r--r--src/network/ssl/qsslsocket_p.h183
-rw-r--r--src/network/ssl/qsslsocket_qt.cpp308
-rw-r--r--src/network/ssl/qsslsocket_schannel.cpp2054
-rw-r--r--src/network/ssl/qsslsocket_schannel_p.h155
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp688
-rw-r--r--src/network/ssl/qsslsocket_winrt_p.h110
-rw-r--r--src/network/ssl/qtlsbackend.cpp2357
-rw-r--r--src/network/ssl/qtlsbackend_p.h402
-rw-r--r--src/network/ssl/qwindowscarootfetcher.cpp168
-rw-r--r--src/network/ssl/qwindowscarootfetcher_p.h79
-rw-r--r--src/network/ssl/ssl.pri131
368 files changed, 26973 insertions, 48715 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
new file mode 100644
index 0000000000..e977400245
--- /dev/null
+++ b/src/network/CMakeLists.txt
@@ -0,0 +1,421 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## Network Module:
+#####################################################################
+
+qt_internal_add_module(Network
+ PLUGIN_TYPES networkaccess networkinformation tls
+ SOURCES
+ access/qabstractnetworkcache.cpp access/qabstractnetworkcache.h access/qabstractnetworkcache_p.h
+ access/qhsts.cpp access/qhsts_p.h
+ access/qhstspolicy.cpp access/qhstspolicy.h
+ access/qnetworkaccessauthenticationmanager.cpp access/qnetworkaccessauthenticationmanager_p.h
+ access/qnetworkaccessbackend.cpp access/qnetworkaccessbackend_p.h
+ access/qnetworkaccesscache.cpp access/qnetworkaccesscache_p.h
+ access/qnetworkaccesscachebackend.cpp access/qnetworkaccesscachebackend_p.h
+ access/qnetworkaccessfilebackend.cpp access/qnetworkaccessfilebackend_p.h
+ access/qnetworkaccessmanager.cpp access/qnetworkaccessmanager.h access/qnetworkaccessmanager_p.h
+ access/qnetworkcookie.cpp access/qnetworkcookie.h access/qnetworkcookie_p.h
+ access/qnetworkcookiejar.cpp access/qnetworkcookiejar.h access/qnetworkcookiejar_p.h
+ access/qnetworkfile.cpp access/qnetworkfile_p.h
+ access/qhttpheaders.cpp access/qhttpheaders.h
+ access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h
+ access/qhttpheadershelper.cpp access/qhttpheadershelper_p.h
+ access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h
+ access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h
+ access/qnetworkreplyfileimpl.cpp access/qnetworkreplyfileimpl_p.h
+ access/qnetworkreplyimpl.cpp access/qnetworkreplyimpl_p.h
+ access/qnetworkrequest.cpp access/qnetworkrequest.h access/qnetworkrequest_p.h
+ compat/removed_api.cpp
+ kernel/qauthenticator.cpp kernel/qauthenticator.h kernel/qauthenticator_p.h
+ kernel/qhostaddress.cpp kernel/qhostaddress.h kernel/qhostaddress_p.h
+ kernel/qhostinfo.cpp kernel/qhostinfo.h kernel/qhostinfo_p.h
+ kernel/qnetconmonitor_p.h
+ kernel/qnetworkdatagram.cpp kernel/qnetworkdatagram.h kernel/qnetworkdatagram_p.h
+ kernel/qnetworkinformation.cpp kernel/qnetworkinformation_p.h kernel/qnetworkinformation.h
+ kernel/qnetworkinterface.cpp kernel/qnetworkinterface.h kernel/qnetworkinterface_p.h
+ kernel/qnetworkinterface_unix_p.h
+ kernel/qnetworkproxy.cpp kernel/qnetworkproxy.h
+ kernel/qtnetworkglobal.h kernel/qtnetworkglobal_p.h
+ socket/qabstractsocket.cpp socket/qabstractsocket.h socket/qabstractsocket_p.h
+ socket/qabstractsocketengine.cpp socket/qabstractsocketengine_p.h
+ socket/qnativesocketengine.cpp socket/qnativesocketengine_p.h socket/qnativesocketengine_p_p.h
+ socket/qtcpserver.cpp socket/qtcpserver.h socket/qtcpserver_p.h
+ socket/qtcpsocket.cpp socket/qtcpsocket.h socket/qtcpsocket_p.h
+ socket/qudpsocket.cpp socket/qudpsocket.h
+ ssl/qpassworddigestor.cpp ssl/qpassworddigestor.h
+ ssl/qssl.cpp ssl/qssl.h ssl/qssl_p.h
+ ssl/qsslcertificate.cpp ssl/qsslcertificate.h ssl/qsslcertificate_p.h
+ ssl/qsslcertificateextension.cpp ssl/qsslcertificateextension.h ssl/qsslcertificateextension_p.h
+ ssl/qsslcipher.h
+ ssl/qsslconfiguration.h
+ ssl/qsslerror.h
+ ssl/qsslkey.h
+ ssl/qsslsocket.h
+ ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
+ DEFINES
+ QT_NO_CONTEXTLESS_CONNECT
+ QT_NO_FOREACH
+ QT_NO_USING_NAMESPACE
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ QT_NO_CAST_FROM_BYTEARRAY
+ QT_NO_URL_CAST_FROM_STRING
+ QT_USE_NODISCARD_FILE_OPEN
+ INCLUDE_DIRECTORIES
+ kernel
+ LIBRARIES
+ Qt::CorePrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ PRIVATE_MODULE_INTERFACE
+ Qt::CorePrivate
+ NO_PCH_SOURCES
+ compat/removed_api.cpp
+ PRECOMPILED_HEADER
+ "../corelib/global/qt_pch.h"
+ GENERATE_CPP_EXPORTS
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_private_tests
+ SOURCES
+ access/qnetworkaccessdebugpipebackend.cpp access/qnetworkaccessdebugpipebackend_p.h
+)
+
+qt_internal_extend_target(Network CONDITION MSVC AND (TEST_architecture_arch STREQUAL "i386")
+ LINK_OPTIONS
+ "/BASE:0x64000000"
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_networkdiskcache
+ SOURCES
+ access/qnetworkdiskcache.cpp access/qnetworkdiskcache.h access/qnetworkdiskcache_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_settings
+ SOURCES
+ access/qhstsstore.cpp access/qhstsstore_p.h
+)
+
+qt_internal_extend_target(Network CONDITION APPLE
+ LIBRARIES
+ ${FWCoreFoundation}
+ ${FWSecurity}
+)
+
+qt_internal_extend_target(Network CONDITION WASM
+ SOURCES
+ access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
+ access/qhttpnetworkheader.cpp access/qhttpnetworkheader_p.h
+ access/qnetworkreplywasmimpl.cpp access/qnetworkreplywasmimpl_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_http
+ SOURCES
+ access/http2/bitstreams.cpp access/http2/bitstreams_p.h
+ access/http2/hpack.cpp access/http2/hpack_p.h
+ access/http2/hpacktable.cpp access/http2/hpacktable_p.h
+ access/http2/http2frames.cpp access/http2/http2frames_p.h
+ access/http2/http2protocol.cpp access/http2/http2protocol_p.h
+ access/http2/http2streams.cpp access/http2/http2streams_p.h
+ access/http2/huffman.cpp access/http2/huffman_p.h
+ access/qabstractprotocolhandler.cpp access/qabstractprotocolhandler_p.h
+ access/qdecompresshelper.cpp access/qdecompresshelper_p.h
+ access/qhttp1configuration.cpp access/qhttp1configuration.h
+ access/qhttp2configuration.cpp access/qhttp2configuration.h
+ access/qhttp2connection.cpp access/qhttp2connection_p.h
+ access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h
+ access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
+ access/qhttpnetworkconnection.cpp access/qhttpnetworkconnection_p.h
+ access/qhttpnetworkconnectionchannel.cpp access/qhttpnetworkconnectionchannel_p.h
+ access/qhttpnetworkheader.cpp access/qhttpnetworkheader_p.h
+ access/qhttpnetworkreply.cpp access/qhttpnetworkreply_p.h
+ access/qhttpnetworkrequest.cpp access/qhttpnetworkrequest_p.h
+ access/qhttpprotocolhandler.cpp access/qhttpprotocolhandler_p.h
+ access/qhttpthreaddelegate.cpp access/qhttpthreaddelegate_p.h
+ access/qnetworkreplyhttpimpl.cpp access/qnetworkreplyhttpimpl_p.h
+ access/qnetworkrequestfactory.cpp access/qnetworkrequestfactory_p.h
+ access/qnetworkrequestfactory.h
+ access/qrestaccessmanager.cpp access/qrestaccessmanager.h access/qrestaccessmanager_p.h
+ access/qrestreply.cpp access/qrestreply.h access/qrestreply_p.h
+ access/qsocketabstraction_p.h
+ socket/qhttpsocketengine.cpp socket/qhttpsocketengine_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_brotli AND QT_FEATURE_http
+ LIBRARIES
+ WrapBrotli::WrapBrotliDec
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_http AND QT_FEATURE_zstd
+ LIBRARIES
+ WrapZSTD::WrapZSTD
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_system_zlib
+ LIBRARIES
+ WrapZLIB::WrapZLIB
+)
+
+qt_internal_extend_target(Network CONDITION NOT QT_FEATURE_system_zlib
+ INCLUDE_DIRECTORIES
+ ../3rdparty/zlib/src
+)
+
+qt_internal_extend_target(Network CONDITION NOT QT_FEATURE_system_zlib AND NOT no_core_dep
+ LIBRARIES
+ Qt::Core
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_topleveldomain
+ SOURCES
+ kernel/qtldurl.cpp kernel/qtldurl_p.h
+ ../3rdparty/libpsl/src/lookup_string_in_fixed_set.c
+ INCLUDE_DIRECTORIES
+ ../3rdparty/libpsl
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup
+ SOURCES
+ kernel/qdnslookup.cpp kernel/qdnslookup.h kernel/qdnslookup_p.h
+)
+
+qt_internal_extend_target(Network CONDITION UNIX
+ SOURCES
+ kernel/qhostinfo_unix.cpp
+ socket/qnativesocketengine_unix.cpp
+ socket/qnet_unix_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_linux_netlink AND UNIX
+ SOURCES
+ kernel/qnetworkinterface_linux.cpp
+)
+
+qt_internal_extend_target(Network CONDITION UNIX AND NOT QT_FEATURE_linux_netlink
+ SOURCES
+ kernel/qnetworkinterface_unix.cpp
+)
+
+qt_internal_extend_target(Network CONDITION WIN32
+ SOURCES
+ kernel/qhostinfo_win.cpp
+ kernel/qnetworkinterface_win.cpp
+ kernel/qnetworkproxy_win.cpp
+ socket/qnativesocketengine_win.cpp
+ LIBRARIES
+ advapi32
+ dnsapi
+ iphlpapi
+ secur32
+ winhttp
+ DEFINES
+ NOMINMAX
+)
+
+qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT
+ LIBRARIES
+ ${FWCoreServices}
+ ${FWSystemConfiguration}
+)
+
+qt_internal_extend_target(Network CONDITION APPLE
+ LIBRARIES
+ ${FWCFNetwork}
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND QT_FEATURE_libresolv
+ SOURCES
+ kernel/qdnslookup_unix.cpp
+ LIBRARIES
+ WrapResolv::WrapResolv
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND WIN32
+ SOURCES
+ kernel/qdnslookup_win.cpp
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND NOT QT_FEATURE_libresolv AND NOT WIN32
+ SOURCES
+ kernel/qdnslookup_dummy.cpp
+)
+
+qt_internal_extend_target(Network CONDITION APPLE
+ SOURCES
+ kernel/qnetconmonitor_darwin.mm
+ LIBRARIES
+ ${FWSystemConfiguration}
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_networklistmanager AND NOT IOS AND NOT MACOS
+ SOURCES
+ kernel/qnetconmonitor_win.cpp
+)
+
+qt_internal_extend_target(Network CONDITION NOT APPLE AND NOT QT_FEATURE_networklistmanager
+ SOURCES
+ kernel/qnetconmonitor_stub.cpp
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_gssapi
+ LIBRARIES
+ GSSAPI::GSSAPI
+)
+
+qt_internal_extend_target(Network CONDITION UIKIT
+ SOURCES
+ kernel/qnetworkinterface_uikit_p.h
+)
+
+qt_internal_extend_target(Network CONDITION APPLE AND NOT VISIONOS
+ SOURCES
+ kernel/qnetworkproxy_darwin.cpp
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_libproxy AND UNIX AND NOT MACOS
+ SOURCES
+ kernel/qnetworkproxy_libproxy.cpp
+ LIBRARIES
+ ${CMAKE_DL_LIBS}
+ PkgConfig::Libproxy
+)
+
+qt_internal_extend_target(Network CONDITION ANDROID
+ SOURCES
+ kernel/qnetworkproxy_android.cpp
+)
+
+qt_internal_extend_target(Network CONDITION UNIX AND NOT ANDROID AND NOT (APPLE AND NOT VISIONOS) AND NOT QT_FEATURE_libproxy AND (UNIX OR WINRT)
+ SOURCES
+ kernel/qnetworkproxy_generic.cpp
+)
+
+if(ANDROID AND (ANDROID))
+ set_property(TARGET Network APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
+ jar/Qt${QtBase_VERSION_MAJOR}AndroidNetwork.jar
+ )
+endif()
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_socks5
+ SOURCES
+ socket/qsocks5socketengine.cpp socket/qsocks5socketengine_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_sctp
+ SOURCES
+ socket/qsctpserver.cpp socket/qsctpserver.h socket/qsctpserver_p.h
+ socket/qsctpsocket.cpp socket/qsctpsocket.h socket/qsctpsocket_p.h
+)
+
+qt_internal_extend_target(Network CONDITION MSVC
+ MOC_OPTIONS
+ "-D_WINSOCK_DEPRECATED_NO_WARNINGS"
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver
+ SOURCES
+ socket/qlocalserver.cpp socket/qlocalserver.h socket/qlocalserver_p.h
+ socket/qlocalsocket.cpp socket/qlocalsocket.h socket/qlocalsocket_p.h
+)
+
+qt_internal_extend_target(Network CONDITION INTEGRITY AND QT_FEATURE_localserver
+ SOURCES
+ socket/qlocalserver_tcp.cpp
+ socket/qlocalsocket_tcp.cpp
+ DEFINES
+ QT_LOCALSOCKET_TCP
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver AND UNIX AND NOT INTEGRITY
+ SOURCES
+ socket/qlocalserver_unix.cpp
+ socket/qlocalsocket_unix.cpp
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver AND WIN32
+ SOURCES
+ socket/qlocalserver_win.cpp
+ socket/qlocalsocket_win.cpp
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30
+ LIBRARIES
+ WrapOpenSSL::WrapOpenSSL
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_system_proxies
+ DEFINES
+ QT_USE_SYSTEM_PROXIES
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_ssl
+ SOURCES
+ ssl/qocspresponse.cpp ssl/qocspresponse.h ssl/qocspresponse_p.h
+ ssl/qsslcipher.cpp ssl/qsslcipher_p.h
+ ssl/qsslconfiguration.cpp ssl/qsslconfiguration_p.h
+ ssl/qssldiffiehellmanparameters.cpp ssl/qssldiffiehellmanparameters.h ssl/qssldiffiehellmanparameters_p.h
+ ssl/qsslellipticcurve.cpp ssl/qsslellipticcurve.h
+ ssl/qsslerror.cpp
+ ssl/qsslkey_p.cpp ssl/qsslkey_p.h
+ ssl/qsslpresharedkeyauthenticator.cpp ssl/qsslpresharedkeyauthenticator.h ssl/qsslpresharedkeyauthenticator_p.h
+ ssl/qsslsocket.cpp ssl/qsslsocket_p.h
+ ssl/qsslserver.cpp ssl/qsslserver.h ssl/qsslserver_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dtls AND QT_FEATURE_ssl
+ SOURCES
+ ssl/qdtls.cpp ssl/qdtls.h ssl/qdtls_p.h
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_ocsp AND QT_FEATURE_openssl AND QT_FEATURE_ssl
+ SOURCES
+ ssl/qocsp_p.h
+)
+
+qt_internal_add_docs(Network
+ doc/qtnetwork.qdocconf
+)
+
+# See mkspecs/common/msvc-desktop.conf
+qt_internal_extend_target(Network CONDITION WIN32 PUBLIC_LIBRARIES ws2_32)
+
+# See mkspecs/common/qcc-base-qnx.conf
+qt_internal_extend_target(Network CONDITION QNX PUBLIC_LIBRARIES socket)
+
+qt_internal_extend_target(Network CONDITION SOLARIS PUBLIC_LIBRARIES socket nsl)
+
+qt_internal_extend_target(Network CONDITION WIN32
+ NO_UNITY_BUILD_SOURCES
+ kernel/qauthenticator.cpp
+ kernel/qdnslookup_win.cpp
+ kernel/qhostaddress.cpp
+ kernel/qhostinfo.cpp
+ kernel/qhostinfo_win.cpp
+ kernel/qnetconmonitor_win.cpp
+ kernel/qnetworkinterface_win.cpp
+ kernel/qnetworkproxy_win.cpp
+ socket/qabstractsocket.cpp
+ socket/qlocalserver.cpp
+ socket/qlocalserver_win.cpp
+ socket/qlocalsocket_win.cpp
+ socket/qnativesocketengine.cpp
+ socket/qnativesocketengine_win.cpp
+)
+
+# include the snippet projects for developer-builds
+if(QT_FEATURE_private_tests)
+ add_subdirectory(doc/snippets/network)
+endif()
+qt_internal_extend_target(Network
+ # Workaround for QTBUG-118229:
+ # Function called by inline methods taking a pointer to a private class as a parameter
+ EXTRA_LINKER_SCRIPT_EXPORTS
+ # QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
+ "_ZN*16QNetworkDatagram7destroyEP*23QNetworkDatagramPrivate*"
+)
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
deleted file mode 100644
index cfb20dcd71..0000000000
--- a/src/network/access/access.pri
+++ /dev/null
@@ -1,124 +0,0 @@
-# Qt network access module
-
-HEADERS += \
- access/qnetworkaccessauthenticationmanager_p.h \
- access/qnetworkaccessmanager.h \
- access/qnetworkaccessmanager_p.h \
- access/qnetworkaccesscache_p.h \
- access/qnetworkaccessbackend_p.h \
- access/qnetworkaccessdebugpipebackend_p.h \
- access/qnetworkaccessfilebackend_p.h \
- access/qnetworkaccesscachebackend_p.h \
- access/qnetworkcookie.h \
- access/qnetworkcookie_p.h \
- access/qnetworkcookiejar.h \
- access/qnetworkcookiejar_p.h \
- access/qnetworkrequest.h \
- access/qnetworkrequest_p.h \
- access/qnetworkreply.h \
- access/qnetworkreply_p.h \
- access/qnetworkreplyimpl_p.h \
- access/qnetworkreplydataimpl_p.h \
- access/qnetworkreplyfileimpl_p.h \
- access/qabstractnetworkcache_p.h \
- access/qabstractnetworkcache.h \
- access/qnetworkfile_p.h \
- access/qhsts_p.h \
- access/qhstspolicy.h
-
-SOURCES += \
- access/qnetworkaccessauthenticationmanager.cpp \
- access/qnetworkaccessmanager.cpp \
- access/qnetworkaccesscache.cpp \
- access/qnetworkaccessbackend.cpp \
- access/qnetworkaccessdebugpipebackend.cpp \
- access/qnetworkaccessfilebackend.cpp \
- access/qnetworkaccesscachebackend.cpp \
- access/qnetworkcookie.cpp \
- access/qnetworkcookiejar.cpp \
- access/qnetworkrequest.cpp \
- access/qnetworkreply.cpp \
- access/qnetworkreplyimpl.cpp \
- access/qnetworkreplydataimpl.cpp \
- access/qnetworkreplyfileimpl.cpp \
- access/qabstractnetworkcache.cpp \
- access/qnetworkfile.cpp \
- access/qhsts.cpp \
- access/qhstspolicy.cpp
-
-qtConfig(ftp) {
- HEADERS += \
- access/qftp_p.h \
- access/qnetworkaccessftpbackend_p.h
-
- SOURCES += \
- access/qftp.cpp \
- access/qnetworkaccessftpbackend.cpp
-}
-
-qtConfig(networkdiskcache) {
- HEADERS += \
- access/qnetworkdiskcache_p.h \
- access/qnetworkdiskcache.h
-
- SOURCES += access/qnetworkdiskcache.cpp
-}
-
-qtConfig(settings) {
- HEADERS += \
- access/qhstsstore_p.h
-
- SOURCES += \
- access/qhstsstore.cpp
-}
-
-mac: LIBS_PRIVATE += -framework Security
-
-wasm {
- SOURCES += \
- access/qnetworkreplywasmimpl.cpp
- HEADERS += \
- access/qnetworkreplywasmimpl_p.h
-}
-
-include($$PWD/../../3rdparty/zlib_dependency.pri)
-
-qtConfig(http) {
- include($$PWD/http2/http2.pri)
-
- SOURCES += \
- access/qabstractprotocolhandler.cpp \
- access/qhttp2protocolhandler.cpp \
- access/qhttpmultipart.cpp \
- access/qhttpnetworkconnection.cpp \
- access/qhttpnetworkconnectionchannel.cpp \
- access/qhttpnetworkheader.cpp \
- access/qhttpnetworkreply.cpp \
- access/qhttpnetworkrequest.cpp \
- access/qhttpprotocolhandler.cpp \
- access/qhttpthreaddelegate.cpp \
- access/qnetworkreplyhttpimpl.cpp \
- access/qhttp2configuration.cpp
-
- HEADERS += \
- access/qabstractprotocolhandler_p.h \
- access/qhttp2protocolhandler_p.h \
- access/qhttpmultipart.h \
- access/qhttpmultipart_p.h \
- access/qhttpnetworkconnection_p.h \
- access/qhttpnetworkconnectionchannel_p.h \
- access/qhttpnetworkheader_p.h \
- access/qhttpnetworkreply_p.h \
- access/qhttpnetworkrequest_p.h \
- access/qhttpprotocolhandler_p.h \
- access/qhttpthreaddelegate_p.h \
- access/qnetworkreplyhttpimpl_p.h \
- access/qhttp2configuration.h
-
- qtConfig(ssl) {
- SOURCES += \
- access/qspdyprotocolhandler.cpp
- HEADERS += \
- access/qspdyprotocolhandler_p.h
- }
-}
diff --git a/src/network/access/http2/bitstreams.cpp b/src/network/access/http2/bitstreams.cpp
index d22c7cd4ec..c35f0e3aaa 100644
--- a/src/network/access/http2/bitstreams.cpp
+++ b/src/network/access/http2/bitstreams.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "huffman_p.h"
@@ -99,7 +63,7 @@ void BitOStream::write(quint32 src)
}
}
-void BitOStream::write(const QByteArray &src, bool compressed)
+void BitOStream::write(QByteArrayView src, bool compressed)
{
quint32 byteLen = src.size();
if (compressed && byteLen) {
diff --git a/src/network/access/http2/bitstreams_p.h b/src/network/access/http2/bitstreams_p.h
index ca272062a6..c96adb5390 100644
--- a/src/network/access/http2/bitstreams_p.h
+++ b/src/network/access/http2/bitstreams_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef BITSTREAMS_P_H
#define BITSTREAMS_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qdebug.h>
#include <type_traits>
@@ -79,7 +43,7 @@ public:
// * 32-bit integers
// * strings
void write(quint32 src);
- void write(const QByteArray &src, bool compressed);
+ void write(QByteArrayView src, bool compressed);
quint64 bitLength() const;
quint64 byteLength() const;
diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp
index b40cc29e1a..9e970dda53 100644
--- a/src/network/access/http2/hpack.cpp
+++ b/src/network/access/http2/hpack.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "hpack_p.h"
@@ -127,7 +91,7 @@ bool read_bit_pattern(const BitPattern &pattern, BitIStream &inputStream)
return true;
}
-bool is_request_pseudo_header(const QByteArray &name)
+bool is_request_pseudo_header(QByteArrayView name)
{
return name == ":method" || name == ":scheme" ||
name == ":authority" || name == ":path";
@@ -230,8 +194,8 @@ bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream,
using size_type = decltype(header.size());
bool methodFound = false;
- const char *headerName[] = {":authority", ":scheme", ":path"};
- const size_type nHeaders = sizeof headerName / sizeof headerName[0];
+ constexpr QByteArrayView headerName[] = {":authority", ":scheme", ":path"};
+ constexpr size_type nHeaders = std::size(headerName);
bool headerFound[nHeaders] = {};
for (const auto &field : header) {
@@ -391,7 +355,8 @@ bool Encoder::encodeLiteralField(BitOStream &outputStream, const BitPattern &fie
QByteArray name;
const bool found = lookupTable.fieldName(nameIndex, &name);
- Q_UNUSED(found) Q_ASSERT(found);
+ Q_UNUSED(found);
+ Q_ASSERT(found);
if (fieldType == LiteralIncrementalIndexing) {
if (!lookupTable.prependField(name, value))
@@ -539,6 +504,49 @@ void Decoder::handleStreamError(BitIStream &inputStream)
// HTTP2 layer will end with session error/COMPRESSION_ERROR.
}
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader)
+{
+ constexpr QByteArrayView names[] = { ":authority", ":method", ":path", ":scheme" };
+ enum PseudoHeaderEnum
+ {
+ Authority,
+ Method,
+ Path,
+ Scheme
+ };
+ std::array<std::optional<QByteArrayView>, std::size(names)> pseudoHeaders{};
+ for (const auto &field : requestHeader) {
+ const auto *it = std::find(std::begin(names), std::end(names), QByteArrayView(field.name));
+ if (it != std::end(names)) {
+ const auto index = std::distance(std::begin(names), it);
+ if (field.value.isEmpty() || pseudoHeaders.at(index).has_value())
+ return {};
+ pseudoHeaders[index] = field.value;
+ }
+ }
+
+ auto optionalIsSet = [](const auto &x) { return x.has_value(); };
+ if (!std::all_of(pseudoHeaders.begin(), pseudoHeaders.end(), optionalIsSet)) {
+ // All four required, HTTP/2 8.1.2.3.
+ return {};
+ }
+
+ const QByteArrayView method = pseudoHeaders[Method].value();
+ if (method.compare("get", Qt::CaseInsensitive) != 0 &&
+ method.compare("head", Qt::CaseInsensitive) != 0) {
+ return {};
+ }
+
+ QUrl url;
+ url.setScheme(QLatin1StringView(pseudoHeaders[Scheme].value()));
+ url.setAuthority(QLatin1StringView(pseudoHeaders[Authority].value()));
+ url.setPath(QLatin1StringView(pseudoHeaders[Path].value()));
+
+ if (!url.isValid())
+ return {};
+ return url;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h
index 8c2701e7af..b407b81941 100644
--- a/src/network/access/http2/hpack_p.h
+++ b/src/network/access/http2/hpack_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HPACK_P_H
#define HPACK_P_H
@@ -54,8 +18,10 @@
#include "hpacktable_p.h"
#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -148,6 +114,7 @@ private:
FieldLookupTable lookupTable;
};
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader);
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp
index fddb5feca5..2c728b37e3 100644
--- a/src/network/access/http2/hpacktable.cpp
+++ b/src/network/access/http2/hpacktable.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "hpacktable_p.h"
@@ -52,7 +16,7 @@ QT_BEGIN_NAMESPACE
namespace HPack
{
-HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
+HeaderSize entry_size(QByteArrayView name, QByteArrayView value)
{
// 32 comes from HPACK:
// "4.1 Calculating Table Size
@@ -62,8 +26,10 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
// for counting the number of references to the name and value would have
// 32 octets of overhead."
- const unsigned sum = unsigned(name.size() + value.size());
- if (std::numeric_limits<unsigned>::max() - 32 < sum)
+ size_t sum;
+ if (qAddOverflow(size_t(name.size()), size_t(value.size()), &sum))
+ return HeaderSize();
+ if (sum > (std::numeric_limits<unsigned>::max() - 32))
return HeaderSize();
return HeaderSize(true, quint32(sum + 32));
}
@@ -183,7 +149,8 @@ bool FieldLookupTable::prependField(const QByteArray &name, const QByteArray &va
if (useIndex) {
const auto result = searchIndex.insert(frontKey());
- Q_UNUSED(result) Q_ASSERT(result.second);
+ Q_UNUSED(result);
+ Q_ASSERT(result.second);
}
return true;
@@ -198,7 +165,8 @@ void FieldLookupTable::evictEntry()
if (useIndex) {
const auto res = searchIndex.erase(backKey());
- Q_UNUSED(res) Q_ASSERT(res == 1);
+ Q_UNUSED(res);
+ Q_ASSERT(res == 1);
}
const HeaderField &field = back();
@@ -380,8 +348,7 @@ quint32 FieldLookupTable::indexOfChunk(const Chunk *chunk) const
return quint32(i);
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h
index 587d86f09c..d57013150b 100644
--- a/src/network/access/http2/hpacktable_p.h
+++ b/src/network/access/http2/hpacktable_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HPACKTABLE_P_H
#define HPACKTABLE_P_H
@@ -52,7 +16,7 @@
//
#include <QtCore/qbytearray.h>
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qpair.h>
#include <vector>
@@ -88,7 +52,7 @@ struct Q_AUTOTEST_EXPORT HeaderField
using HeaderSize = QPair<bool, quint32>;
-HeaderSize entry_size(const QByteArray &name, const QByteArray &value);
+HeaderSize entry_size(QByteArrayView name, QByteArrayView value);
inline HeaderSize entry_size(const HeaderField &entry)
{
diff --git a/src/network/access/http2/http2.pri b/src/network/access/http2/http2.pri
deleted file mode 100644
index e9f30aeb4a..0000000000
--- a/src/network/access/http2/http2.pri
+++ /dev/null
@@ -1,17 +0,0 @@
-HEADERS += \
- access/http2/bitstreams_p.h \
- access/http2/huffman_p.h \
- access/http2/hpack_p.h \
- access/http2/hpacktable_p.h \
- access/http2/http2frames_p.h \
- access/http2/http2streams_p.h \
- access/http2/http2protocol_p.h
-
-SOURCES += \
- access/http2/bitstreams.cpp \
- access/http2/huffman.cpp \
- access/http2/hpack.cpp \
- access/http2/hpacktable.cpp \
- access/http2/http2frames.cpp \
- access/http2/http2streams.cpp \
- access/http2/http2protocol.cpp
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index ce33505683..e07c96b803 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2frames_p.h"
@@ -171,6 +135,7 @@ FrameStatus Frame::validateHeader() const
// 6.6 PUSH_PROMISE
if (framePayloadSize < 4)
return FrameStatus::sizeError;
+ break;
default:
// DATA/HEADERS/CONTINUATION will be verified
// when we have payload.
@@ -233,7 +198,8 @@ quint32 Frame::dataSize() const
Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
quint32 size = payloadSize();
- if (const uchar pad = padding()) {
+ if (flags().testFlag(FrameFlag::PADDED)) {
+ const uchar pad = padding();
// + 1 one for a byte with padding number itself:
size -= pad + 1;
}
@@ -269,7 +235,7 @@ const uchar *Frame::dataBegin() const
return nullptr;
const uchar *src = &buffer[0] + frameHeaderSize;
- if (padding())
+ if (flags().testFlag(FrameFlag::PADDED))
++src;
if (priority())
@@ -293,7 +259,7 @@ const uchar *Frame::hpackBlockBegin() const
return begin;
}
-FrameStatus FrameReader::read(QAbstractSocket &socket)
+FrameStatus FrameReader::read(QIODevice &socket)
{
if (offset < frameHeaderSize) {
if (!readHeader(socket))
@@ -321,7 +287,7 @@ FrameStatus FrameReader::read(QAbstractSocket &socket)
return frame.validatePayload();
}
-bool FrameReader::readHeader(QAbstractSocket &socket)
+bool FrameReader::readHeader(QIODevice &socket)
{
Q_ASSERT(offset < frameHeaderSize);
@@ -337,7 +303,7 @@ bool FrameReader::readHeader(QAbstractSocket &socket)
return offset == frameHeaderSize;
}
-bool FrameReader::readPayload(QAbstractSocket &socket)
+bool FrameReader::readPayload(QIODevice &socket)
{
Q_ASSERT(offset < frame.buffer.size());
Q_ASSERT(frame.buffer.size() > frameHeaderSize);
@@ -428,7 +394,7 @@ void FrameWriter::updatePayloadSize()
setPayloadSize(size);
}
-bool FrameWriter::write(QAbstractSocket &socket) const
+bool FrameWriter::write(QIODevice &socket) const
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -442,7 +408,7 @@ bool FrameWriter::write(QAbstractSocket &socket) const
return nWritten != -1 && size_type(nWritten) == buffer.size();
}
-bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
+bool FrameWriter::writeHEADERS(QIODevice &socket, quint32 sizeLimit)
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -492,7 +458,7 @@ bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
return true;
}
-bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+bool FrameWriter::writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size)
{
// With DATA frame(s) we always have:
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
index 4bdc775806..48e3f751b7 100644
--- a/src/network/access/http2/http2frames_p.h
+++ b/src/network/access/http2/http2frames_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2FRAMES_P_H
#define HTTP2FRAMES_P_H
@@ -63,7 +27,7 @@
QT_BEGIN_NAMESPACE
class QHttp2ProtocolHandler;
-class QAbstractSocket;
+class QIODevice;
namespace Http2
{
@@ -101,15 +65,15 @@ struct Q_AUTOTEST_EXPORT Frame
class Q_AUTOTEST_EXPORT FrameReader
{
public:
- FrameStatus read(QAbstractSocket &socket);
+ FrameStatus read(QIODevice &socket);
Frame &inboundFrame()
{
return frame;
}
private:
- bool readHeader(QAbstractSocket &socket);
- bool readPayload(QAbstractSocket &socket);
+ bool readHeader(QIODevice &socket);
+ bool readPayload(QIODevice &socket);
quint32 offset = 0;
Frame frame;
@@ -159,20 +123,25 @@ public:
{
append(&payload[0], &payload[0] + payload.size());
}
+ void append(QByteArrayView payload)
+ {
+ append(reinterpret_cast<const uchar *>(payload.begin()),
+ reinterpret_cast<const uchar *>(payload.end()));
+ }
void append(const uchar *begin, const uchar *end);
// Write as a single frame:
- bool write(QAbstractSocket &socket) const;
+ bool write(QIODevice &socket) const;
// Two types of frames we are sending are affected by frame size limits:
// HEADERS and DATA. HEADERS' payload (hpacked HTTP headers, following a
// frame header) is always in our 'buffer', we send the initial HEADERS
// frame first and then CONTINUTATION frame(s) if needed:
- bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
+ bool writeHEADERS(QIODevice &socket, quint32 sizeLimit);
// With DATA frames the actual payload is never in our 'buffer', it's a
// 'readPointer' from QNonContiguousData. We split this payload as needed
// into DATA frames with correct payload size fitting into frame size limit:
- bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ bool writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size);
private:
void updatePayloadSize();
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
index 31da6fd616..8e7e176c41 100644
--- a/src/network/access/http2/http2protocol.cpp
+++ b/src/network/access/http2/http2protocol.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2protocol_p.h"
#include "http2frames_p.h"
@@ -50,6 +14,10 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(Http2::Settings, Http2__Settings)
+
Q_LOGGING_CATEGORY(QT_HTTP2, "qt.network.http2")
namespace Http2
@@ -71,9 +39,12 @@ Frame configurationToSettingsFrame(const QHttp2Configuration &config)
// Server push:
builder.append(Settings::ENABLE_PUSH_ID);
builder.append(int(config.serverPushEnabled()));
- // Stream receive window size:
- builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
- builder.append(config.streamReceiveWindowSize());
+
+ // Stream receive window size (if it's a default value, don't include):
+ if (config.streamReceiveWindowSize() != defaultSessionWindowSize) {
+ builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
+ builder.append(config.streamReceiveWindowSize());
+ }
if (config.maxFrameSize() != minPayloadLimit) {
builder.append(Settings::MAX_FRAME_SIZE_ID);
@@ -105,12 +76,10 @@ void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetwor
Q_ASSERT(request);
// RFC 2616, 14.10
// RFC 7540, 3.2
- QByteArray value(request->headerField("Connection"));
+ const QByteArray connectionHeader = request->headerField("Connection");
+ const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(", ");
// We _append_ 'Upgrade':
- if (value.size())
- value += ", ";
-
- value += "Upgrade, HTTP2-Settings";
+ QByteArray value = connectionHeader + separator + "Upgrade, HTTP2-Settings";
request->setHeaderField("Connection", value);
// This we just (re)write.
request->setHeaderField("Upgrade", "h2c");
@@ -125,7 +94,7 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
{
if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("RST_STREAM with unknown error code (%1)");
+ errorMessage = "RST_STREAM with unknown error code (%1)"_L1;
errorMessage = errorMessage.arg(errorCode);
return;
}
@@ -139,61 +108,61 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
break;
case PROTOCOL_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("HTTP/2 protocol error");
+ errorMessage = "HTTP/2 protocol error"_L1;
break;
case INTERNAL_ERROR:
error = QNetworkReply::InternalServerError;
- errorMessage = QLatin1String("Internal server error");
+ errorMessage = "Internal server error"_L1;
break;
case FLOW_CONTROL_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Flow control error");
+ errorMessage = "Flow control error"_L1;
break;
case SETTINGS_TIMEOUT:
error = QNetworkReply::TimeoutError;
- errorMessage = QLatin1String("SETTINGS ACK timeout error");
+ errorMessage = "SETTINGS ACK timeout error"_L1;
break;
case STREAM_CLOSED:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server received frame(s) on a half-closed stream");
+ errorMessage = "Server received frame(s) on a half-closed stream"_L1;
break;
case FRAME_SIZE_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server received a frame with an invalid size");
+ errorMessage = "Server received a frame with an invalid size"_L1;
break;
case REFUSE_STREAM:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server refused a stream");
+ errorMessage = "Server refused a stream"_L1;
break;
case CANCEL:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Stream is no longer needed");
+ errorMessage = "Stream is no longer needed"_L1;
break;
case COMPRESSION_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server is unable to maintain the "
- "header compression context for the connection");
+ errorMessage = "Server is unable to maintain the "
+ "header compression context for the connection"_L1;
break;
case CONNECT_ERROR:
// TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
error = QNetworkReply::UnknownNetworkError;
- errorMessage = QLatin1String("The connection established in response "
- "to a CONNECT request was reset or abnormally closed");
+ errorMessage = "The connection established in response "
+ "to a CONNECT request was reset or abnormally closed"_L1;
break;
case ENHANCE_YOUR_CALM:
error = QNetworkReply::UnknownServerError;
- errorMessage = QLatin1String("Server dislikes our behavior, excessive load detected.");
+ errorMessage = "Server dislikes our behavior, excessive load detected."_L1;
break;
case INADEQUATE_SECURITY:
error = QNetworkReply::ContentAccessDenied;
- errorMessage = QLatin1String("The underlying transport has properties "
- "that do not meet minimum security "
- "requirements");
+ errorMessage = "The underlying transport has properties "
+ "that do not meet minimum security "
+ "requirements"_L1;
break;
case HTTP_1_1_REQUIRED:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server requires that HTTP/1.1 "
- "be used instead of HTTP/2.");
+ errorMessage = "Server requires that HTTP/1.1 "
+ "be used instead of HTTP/2."_L1;
}
}
@@ -215,19 +184,45 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
{
- if (reply.statusCode() == 101) {
- // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
- const auto &header = reply.header();
- for (const QPair<QByteArray, QByteArray> &field : header) {
- if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 &&
- field.second.compare("h2c", Qt::CaseInsensitive) == 0)
- return true;
- }
+ if (reply.statusCode() != 101)
+ return false;
+
+ // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
+ for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
+ if (v.compare("h2c", Qt::CaseInsensitive) == 0)
+ return true;
}
return false;
}
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames)
+{
+ std::vector<uchar> hpackBlock;
+
+ size_t total = 0;
+ for (const auto &frame : frames) {
+ if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
+ return hpackBlock;
+ }
+
+ if (!total)
+ return hpackBlock;
+
+ hpackBlock.resize(total);
+ auto dst = hpackBlock.begin();
+ for (const auto &frame : frames) {
+ if (const auto hpackBlockSize = frame.hpackBlockSize()) {
+ const uchar *src = frame.hpackBlockBegin();
+ std::copy(src, src + hpackBlockSize, dst);
+ dst += hpackBlockSize;
+ }
+ }
+
+ return hpackBlock;
+}
+
+
} // namespace Http2
QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index b0af5aa919..f0f18d1dd5 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2PROTOCOL_P_H
#define HTTP2PROTOCOL_P_H
@@ -54,9 +18,11 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qmetatype.h>
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qmap.h>
+#include <vector>
+
// Different HTTP/2 constants/values as defined by RFC 7540.
QT_BEGIN_NAMESPACE
@@ -133,9 +99,6 @@ enum Http2PredefinedParameters
maxPayloadSize = (1 << 24) - 1, // HTTP/2 6.5.2
defaultSessionWindowSize = 65535, // HTTP/2 6.5.2
- // Using 1000 (rather arbitrarily), just to
- // impose *some* upper limit:
- maxPeerConcurrentStreams = 1000,
maxConcurrentStreams = 100 // HTTP/2, 6.5.2
};
@@ -151,11 +114,13 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1
// HTTP/2 servers are not afraid to immediately set it to the possible max,
// we do the same and split this window size between our concurrent streams.
const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1);
-const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams;
+// Presumably, we never use up to 100 streams so let it be 10 simultaneous:
+const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / 10;
-struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration);
+struct Frame Q_AUTOTEST_EXPORT configurationToSettingsFrame(const QHttp2Configuration &configuration);
QByteArray settingsFrameToBase64(const Frame &settingsFrame);
void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request);
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames);
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
@@ -203,6 +168,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(Http2::Settings)
+QT_DECL_METATYPE_EXTERN_TAGGED(Http2::Settings, Http2__Settings, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/http2/http2streams.cpp b/src/network/access/http2/http2streams.cpp
index fa39c1d57b..3de8b946fe 100644
--- a/src/network/access/http2/http2streams.cpp
+++ b/src/network/access/http2/http2streams.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2streams_p.h"
diff --git a/src/network/access/http2/http2streams_p.h b/src/network/access/http2/http2streams_p.h
index 0be6b3b253..e3745cd2d4 100644
--- a/src/network/access/http2/http2streams_p.h
+++ b/src/network/access/http2/http2streams_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2STREAMS_P_H
#define HTTP2STREAMS_P_H
diff --git a/src/network/access/http2/huffman.cpp b/src/network/access/http2/huffman.cpp
index 0c1aa54dd6..e957c3311a 100644
--- a/src/network/access/http2/huffman.cpp
+++ b/src/network/access/http2/huffman.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "huffman_p.h"
@@ -78,7 +42,7 @@ namespace HPack
...
[00001 | 4 remaining bits]
- All entires with indices between these two will 'point' to value 48
+ All entries with indices between these two will 'point' to value 48
with bitLength == 5 so that bit stream (for example) 000001010 will be
decoded as: 48 + "put 1010 back into bitstream".
@@ -381,7 +345,7 @@ void write_huffman_code(BitOStream &outputStream, const CodeEntry &code)
// That's from HPACK's specs - we deal with octets.
static_assert(std::numeric_limits<uchar>::digits == 8, "octets expected");
-quint64 huffman_encoded_bit_length(const QByteArray &inputData)
+quint64 huffman_encoded_bit_length(QByteArrayView inputData)
{
quint64 bitLength = 0;
for (int i = 0, e = inputData.size(); i < e; ++i)
@@ -390,16 +354,19 @@ quint64 huffman_encoded_bit_length(const QByteArray &inputData)
return bitLength;
}
-void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream)
+void huffman_encode_string(QByteArrayView inputData, BitOStream &outputStream)
{
- for (int i = 0, e = inputData.size(); i < e; ++i)
- write_huffman_code(outputStream, staticHuffmanCodeTable[int(inputData[i])]);
+ for (int i = 0, e = inputData.size(); i < e; ++i) {
+ const auto value = uchar(inputData[i]);
+ write_huffman_code(outputStream, staticHuffmanCodeTable[value]);
+ }
// Pad bits ...
if (outputStream.bitLength() % 8)
outputStream.writeBits(0xff, 8 - outputStream.bitLength() % 8);
}
+static constexpr
bool padding_is_valid(quint32 chunk, quint32 nBits)
{
Q_ASSERT(nBits);
diff --git a/src/network/access/http2/huffman_p.h b/src/network/access/http2/huffman_p.h
index c5324d42b1..daa2b31bb3 100644
--- a/src/network/access/http2/huffman_p.h
+++ b/src/network/access/http2/huffman_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HUFFMAN_P_H
#define HUFFMAN_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -69,8 +33,8 @@ struct CodeEntry
class BitOStream;
-quint64 huffman_encoded_bit_length(const QByteArray &inputData);
-void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream);
+quint64 huffman_encoded_bit_length(QByteArrayView inputData);
+void huffman_encode_string(QByteArrayView inputData, BitOStream &outputStream);
// PrefixTable:
// Huffman codes with a small bit length
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
index 4e217294c4..3cd55d46fa 100644
--- a/src/network/access/qabstractnetworkcache.cpp
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -1,48 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qabstractnetworkcache.h"
#include "qabstractnetworkcache_p.h"
+#include "qnetworkrequest_p.h"
+#include "qhttpheadershelper_p.h"
#include <qdatastream.h>
#include <qdatetime.h>
#include <qurl.h>
+#include <qhash.h>
#include <qdebug.h>
@@ -63,14 +30,14 @@ public:
url == other.url
&& lastModified == other.lastModified
&& expirationDate == other.expirationDate
- && headers == other.headers
- && saveToDisk == other.saveToDisk;
+ && saveToDisk == other.saveToDisk
+ && QHttpHeadersHelper::compareStrict(headers, other.headers);
}
QUrl url;
QDateTime lastModified;
QDateTime expirationDate;
- QNetworkCacheMetaData::RawHeaderList headers;
+ QHttpHeaders headers;
QNetworkCacheMetaData::AttributesMap attributes;
bool saveToDisk;
@@ -232,30 +199,55 @@ QUrl QNetworkCacheMetaData::url() const
*/
void QNetworkCacheMetaData::setUrl(const QUrl &url)
{
- d->url = url;
- d->url.setPassword(QString());
- d->url.setFragment(QString());
+ auto *p = d.data();
+ p->url = url;
+ p->url.setPassword(QString());
+ p->url.setFragment(QString());
}
/*!
Returns a list of all raw headers that are set in this meta data.
The list is in the same order that the headers were set.
- \sa setRawHeaders()
+ \sa setRawHeaders(), headers()
*/
QNetworkCacheMetaData::RawHeaderList QNetworkCacheMetaData::rawHeaders() const
{
- return d->headers;
+ return QNetworkHeadersPrivate::fromHttpToRaw(d->headers);
}
/*!
Sets the raw headers to \a list.
- \sa rawHeaders()
+ \sa rawHeaders(), setHeaders()
*/
void QNetworkCacheMetaData::setRawHeaders(const RawHeaderList &list)
{
- d->headers = list;
+ d->headers = QNetworkHeadersPrivate::fromRawToHttp(list);
+}
+
+/*!
+ \since 6.8
+
+ Returns headers in form of QHttpHeaders that are set in this meta data.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkCacheMetaData::headers() const
+{
+ return d->headers;
+}
+
+/*!
+ \since 6.8
+
+ Sets the headers of this network cache meta data to \a headers.
+
+ \sa headers()
+*/
+void QNetworkCacheMetaData::setHeaders(const QHttpHeaders &headers)
+{
+ d->headers = headers;
}
/*!
@@ -395,12 +387,14 @@ static inline QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData::At
void QNetworkCacheMetaDataPrivate::load(QDataStream &in, QNetworkCacheMetaData &metaData)
{
- in >> metaData.d->url;
- in >> metaData.d->expirationDate;
- in >> metaData.d->lastModified;
- in >> metaData.d->saveToDisk;
- in >> metaData.d->attributes;
- in >> metaData.d->headers;
+ auto *p = metaData.d.data();
+ in >> p->url;
+ in >> p->expirationDate;
+ in >> p->lastModified;
+ in >> p->saveToDisk;
+ in >> p->attributes;
+ QNetworkCacheMetaData::RawHeaderList list; in >> list;
+ metaData.setRawHeaders(list);
}
/*!
@@ -542,3 +536,5 @@ QAbstractNetworkCache::~QAbstractNetworkCache()
*/
QT_END_NAMESPACE
+
+#include "moc_qabstractnetworkcache.cpp"
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index e357dfe58f..b12fd4f863 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTNETWORKCACHE_H
#define QABSTRACTNETWORKCACHE_H
@@ -52,7 +16,6 @@ QT_BEGIN_NAMESPACE
class QIODevice;
class QDateTime;
class QUrl;
-template<class T> class QList;
class QNetworkCacheMetaDataPrivate;
class Q_NETWORK_EXPORT QNetworkCacheMetaData
@@ -71,7 +34,7 @@ public:
QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other);
void swap(QNetworkCacheMetaData &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
bool operator==(const QNetworkCacheMetaData &other) const;
inline bool operator!=(const QNetworkCacheMetaData &other) const
@@ -85,6 +48,9 @@ public:
RawHeaderList rawHeaders() const;
void setRawHeaders(const RawHeaderList &headers);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &headers);
+
QDateTime lastModified() const;
void setLastModified(const QDateTime &dateTime);
diff --git a/src/network/access/qabstractnetworkcache_p.h b/src/network/access/qabstractnetworkcache_p.h
index fee723e315..625cfcb1c3 100644
--- a/src/network/access/qabstractnetworkcache_p.h
+++ b/src/network/access/qabstractnetworkcache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTNETWORKCACHE_P_H
#define QABSTRACTNETWORKCACHE_P_H
diff --git a/src/network/access/qabstractprotocolhandler.cpp b/src/network/access/qabstractprotocolhandler.cpp
index 6847816ba7..5e5019901c 100644
--- a/src/network/access/qabstractprotocolhandler.cpp
+++ b/src/network/access/qabstractprotocolhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qabstractprotocolhandler_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
index 04a07734dd..da5eaeeb74 100644
--- a/src/network/access/qabstractprotocolhandler_p.h
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTPROTOCOLHANDLER_H
#define QABSTRACTPROTOCOLHANDLER_H
@@ -59,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QHttpNetworkConnectionChannel;
class QHttpNetworkReply;
-class QAbstractSocket;
+class QIODevice;
class QHttpNetworkConnection;
class QAbstractProtocolHandler {
@@ -75,7 +39,7 @@ public:
protected:
QHttpNetworkConnectionChannel *m_channel;
QHttpNetworkReply *m_reply;
- QAbstractSocket *m_socket;
+ QIODevice *m_socket;
QHttpNetworkConnection *m_connection;
};
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
new file mode 100644
index 0000000000..52a0d9fc06
--- /dev/null
+++ b/src/network/access/qdecompresshelper.cpp
@@ -0,0 +1,781 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qdecompresshelper_p.h"
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <limits>
+#include <zlib.h>
+
+#if QT_CONFIG(brotli)
+# include <brotli/decode.h>
+#endif
+
+#if QT_CONFIG(zstd)
+# include <zstd.h>
+#endif
+
+#include <array>
+
+QT_BEGIN_NAMESPACE
+namespace {
+struct ContentEncodingMapping
+{
+ QByteArrayView name;
+ QDecompressHelper::ContentEncoding encoding;
+};
+
+constexpr ContentEncodingMapping contentEncodingMapping[] {
+#if QT_CONFIG(zstd)
+ { "zstd", QDecompressHelper::Zstandard },
+#endif
+#if QT_CONFIG(brotli)
+ { "br", QDecompressHelper::Brotli },
+#endif
+ { "gzip", QDecompressHelper::GZip },
+ { "deflate", QDecompressHelper::Deflate },
+};
+
+QDecompressHelper::ContentEncoding encodingFromByteArray(QByteArrayView ce) noexcept
+{
+ for (const auto &mapping : contentEncodingMapping) {
+ if (ce.compare(mapping.name, Qt::CaseInsensitive) == 0)
+ return mapping.encoding;
+ }
+ return QDecompressHelper::None;
+}
+
+z_stream *toZlibPointer(void *ptr)
+{
+ return static_cast<z_stream_s *>(ptr);
+}
+
+#if QT_CONFIG(brotli)
+BrotliDecoderState *toBrotliPointer(void *ptr)
+{
+ return static_cast<BrotliDecoderState *>(ptr);
+}
+#endif
+
+#if QT_CONFIG(zstd)
+ZSTD_DStream *toZstandardPointer(void *ptr)
+{
+ return static_cast<ZSTD_DStream *>(ptr);
+}
+#endif
+}
+
+bool QDecompressHelper::isSupportedEncoding(QByteArrayView encoding)
+{
+ return encodingFromByteArray(encoding) != QDecompressHelper::None;
+}
+
+QByteArrayList QDecompressHelper::acceptedEncoding()
+{
+ QByteArrayList list;
+ list.reserve(std::size(contentEncodingMapping));
+ for (const auto &mapping : contentEncodingMapping) {
+ list << mapping.name.toByteArray();
+ }
+ return list;
+}
+
+QDecompressHelper::~QDecompressHelper()
+{
+ clear();
+}
+
+bool QDecompressHelper::setEncoding(QByteArrayView encoding)
+{
+ Q_ASSERT(contentEncoding == QDecompressHelper::None);
+ if (contentEncoding != QDecompressHelper::None) {
+ qWarning("Encoding is already set.");
+ // This isn't an error, so it doesn't set errorStr, it's just wrong usage.
+ return false;
+ }
+ ContentEncoding ce = encodingFromByteArray(encoding);
+ if (ce == None) {
+ errorStr = QCoreApplication::translate("QHttp", "Unsupported content encoding: %1")
+ .arg(QLatin1String(encoding));
+ return false;
+ }
+ errorStr = QString(); // clear error
+ return setEncoding(ce);
+}
+
+bool QDecompressHelper::setEncoding(ContentEncoding ce)
+{
+ Q_ASSERT(contentEncoding == None);
+ contentEncoding = ce;
+ switch (contentEncoding) {
+ case None:
+ Q_UNREACHABLE();
+ break;
+ case Deflate:
+ case GZip: {
+ z_stream *inflateStream = new z_stream;
+ memset(inflateStream, 0, sizeof(z_stream));
+ // "windowBits can also be greater than 15 for optional gzip decoding.
+ // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
+ // http://www.zlib.net/manual.html
+ if (inflateInit2(inflateStream, MAX_WBITS + 32) != Z_OK) {
+ delete inflateStream;
+ inflateStream = nullptr;
+ }
+ decoderPointer = inflateStream;
+ break;
+ }
+ case Brotli:
+#if QT_CONFIG(brotli)
+ decoderPointer = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+#else
+ Q_UNREACHABLE();
+#endif
+ break;
+ case Zstandard:
+#if QT_CONFIG(zstd)
+ decoderPointer = ZSTD_createDStream();
+#else
+ Q_UNREACHABLE();
+#endif
+ break;
+ }
+ if (!decoderPointer) {
+ errorStr = QCoreApplication::translate("QHttp",
+ "Failed to initialize the compression decoder.");
+ contentEncoding = QDecompressHelper::None;
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Returns true if the QDecompressHelper is measuring the
+ size of the decompressed data.
+
+ \sa setCountingBytesEnabled, uncompressedSize
+*/
+bool QDecompressHelper::isCountingBytes() const
+{
+ return countDecompressed;
+}
+
+/*!
+ \internal
+
+ Enable or disable counting the decompressed size of the data
+ based on \a shouldCount. Enabling this means the data will be
+ decompressed twice (once for counting and once when data is
+ being read).
+
+ \note Can only be called before contentEncoding is set and data
+ is fed to the object.
+
+ \sa isCountingBytes, uncompressedSize
+*/
+void QDecompressHelper::setCountingBytesEnabled(bool shouldCount)
+{
+ // These are a best-effort check to ensure that no data has already been processed before this
+ // gets enabled
+ Q_ASSERT(compressedDataBuffer.byteAmount() == 0);
+ Q_ASSERT(contentEncoding == None);
+ countDecompressed = shouldCount;
+}
+
+/*!
+ \internal
+
+ Returns the amount of uncompressed bytes left.
+
+ \note Since this is only based on the data received
+ so far the final size could be larger.
+
+ \note It is only valid to call this if isCountingBytes()
+ returns true
+
+ \sa isCountingBytes, setCountBytes
+*/
+qint64 QDecompressHelper::uncompressedSize() const
+{
+ Q_ASSERT(countDecompressed);
+ // Use the 'totalUncompressedBytes' from the countHelper if it exceeds the amount of bytes
+ // that we know about.
+ auto totalUncompressed =
+ countHelper && countHelper->totalUncompressedBytes > totalUncompressedBytes
+ ? countHelper->totalUncompressedBytes
+ : totalUncompressedBytes;
+ return totalUncompressed - totalBytesRead;
+}
+
+/*!
+ \internal
+ \overload
+*/
+void QDecompressHelper::feed(const QByteArray &data)
+{
+ return feed(QByteArray(data));
+}
+
+/*!
+ \internal
+ Give \a data to the QDecompressHelper which will be stored until
+ a read is attempted.
+
+ If \c isCountingBytes() is true then it will decompress immediately
+ before discarding the data, but will count the uncompressed byte
+ size.
+*/
+void QDecompressHelper::feed(QByteArray &&data)
+{
+ Q_ASSERT(contentEncoding != None);
+ totalCompressedBytes += data.size();
+ compressedDataBuffer.append(std::move(data));
+ if (!countInternal(compressedDataBuffer[compressedDataBuffer.bufferCount() - 1]))
+ clear(); // If our counting brother failed then so will we :|
+}
+
+/*!
+ \internal
+ \overload
+*/
+void QDecompressHelper::feed(const QByteDataBuffer &buffer)
+{
+ Q_ASSERT(contentEncoding != None);
+ totalCompressedBytes += buffer.byteAmount();
+ compressedDataBuffer.append(buffer);
+ if (!countInternal(buffer))
+ clear(); // If our counting brother failed then so will we :|
+}
+
+/*!
+ \internal
+ \overload
+*/
+void QDecompressHelper::feed(QByteDataBuffer &&buffer)
+{
+ Q_ASSERT(contentEncoding != None);
+ totalCompressedBytes += buffer.byteAmount();
+ const QByteDataBuffer copy(buffer);
+ compressedDataBuffer.append(std::move(buffer));
+ if (!countInternal(copy))
+ clear(); // If our counting brother failed then so will we :|
+}
+
+/*!
+ \internal
+ Decompress the data internally and immediately discard the
+ uncompressed data, but count how many bytes were decoded.
+ This lets us know the final size, unfortunately at the cost of
+ increased computation.
+
+ To save on some of the computation we will store the data until
+ we reach \c MaxDecompressedDataBufferSize stored. In this case the
+ "penalty" is completely removed from users who read the data on
+ readyRead rather than waiting for it all to be received. And
+ any file smaller than \c MaxDecompressedDataBufferSize will
+ avoid this issue as well.
+*/
+bool QDecompressHelper::countInternal()
+{
+ Q_ASSERT(countDecompressed);
+ while (hasDataInternal()
+ && decompressedDataBuffer.byteAmount() < MaxDecompressedDataBufferSize) {
+ const qsizetype toRead = 256 * 1024;
+ QByteArray buffer(toRead, Qt::Uninitialized);
+ qsizetype bytesRead = readInternal(buffer.data(), buffer.size());
+ if (bytesRead == -1)
+ return false;
+ buffer.truncate(bytesRead);
+ decompressedDataBuffer.append(std::move(buffer));
+ }
+ if (!hasDataInternal())
+ return true; // handled all the data so far, just return
+
+ while (countHelper->hasData()) {
+ std::array<char, 1024> temp;
+ qsizetype bytesRead = countHelper->read(temp.data(), temp.size());
+ if (bytesRead == -1)
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+ \overload
+*/
+bool QDecompressHelper::countInternal(const QByteArray &data)
+{
+ if (countDecompressed) {
+ if (!countHelper) {
+ countHelper = std::make_unique<QDecompressHelper>();
+ countHelper->setDecompressedSafetyCheckThreshold(archiveBombCheckThreshold);
+ countHelper->setEncoding(contentEncoding);
+ }
+ countHelper->feed(data);
+ return countInternal();
+ }
+ return true;
+}
+
+/*!
+ \internal
+ \overload
+*/
+bool QDecompressHelper::countInternal(const QByteDataBuffer &buffer)
+{
+ if (countDecompressed) {
+ if (!countHelper) {
+ countHelper = std::make_unique<QDecompressHelper>();
+ countHelper->setDecompressedSafetyCheckThreshold(archiveBombCheckThreshold);
+ countHelper->setEncoding(contentEncoding);
+ }
+ countHelper->feed(buffer);
+ return countInternal();
+ }
+ return true;
+}
+
+qsizetype QDecompressHelper::read(char *data, qsizetype maxSize)
+{
+ if (maxSize <= 0)
+ return 0;
+
+ if (!isValid())
+ return -1;
+
+ if (!hasData())
+ return 0;
+
+ qsizetype cachedRead = 0;
+ if (!decompressedDataBuffer.isEmpty()) {
+ cachedRead = decompressedDataBuffer.read(data, maxSize);
+ data += cachedRead;
+ maxSize -= cachedRead;
+ }
+
+ qsizetype bytesRead = readInternal(data, maxSize);
+ if (bytesRead == -1)
+ return -1;
+ totalBytesRead += bytesRead + cachedRead;
+ return bytesRead + cachedRead;
+}
+
+/*!
+ \internal
+ Like read() but without attempting to read the
+ cached/already-decompressed data.
+*/
+qsizetype QDecompressHelper::readInternal(char *data, qsizetype maxSize)
+{
+ Q_ASSERT(isValid());
+
+ if (maxSize <= 0)
+ return 0;
+
+ if (!hasDataInternal())
+ return 0;
+
+ qsizetype bytesRead = -1;
+ switch (contentEncoding) {
+ case None:
+ Q_UNREACHABLE();
+ break;
+ case Deflate:
+ case GZip:
+ bytesRead = readZLib(data, maxSize);
+ break;
+ case Brotli:
+ bytesRead = readBrotli(data, maxSize);
+ break;
+ case Zstandard:
+ bytesRead = readZstandard(data, maxSize);
+ break;
+ }
+ if (bytesRead == -1)
+ clear();
+
+ totalUncompressedBytes += bytesRead;
+ if (isPotentialArchiveBomb()) {
+ errorStr = QCoreApplication::translate(
+ "QHttp",
+ "The decompressed output exceeds the limits specified by "
+ "QNetworkRequest::decompressedSafetyCheckThreshold()");
+ return -1;
+ }
+
+ return bytesRead;
+}
+
+/*!
+ \internal
+ Set the \a threshold required before the archive bomb detection kicks in.
+ By default this is 10MB. Setting it to -1 is treated as disabling the
+ feature.
+*/
+void QDecompressHelper::setDecompressedSafetyCheckThreshold(qint64 threshold)
+{
+ if (threshold == -1)
+ threshold = std::numeric_limits<qint64>::max();
+ archiveBombCheckThreshold = threshold;
+}
+
+bool QDecompressHelper::isPotentialArchiveBomb() const
+{
+ if (totalCompressedBytes == 0)
+ return false;
+
+ if (totalUncompressedBytes <= archiveBombCheckThreshold)
+ return false;
+
+ // Some protection against malicious or corrupted compressed files that expand far more than
+ // is reasonable.
+ double ratio = double(totalUncompressedBytes) / double(totalCompressedBytes);
+ switch (contentEncoding) {
+ case None:
+ Q_UNREACHABLE();
+ break;
+ case Deflate:
+ case GZip:
+ // This value is mentioned in docs for
+ // QNetworkRequest::setMinimumArchiveBombSize, keep synchronized
+ if (ratio > 40) {
+ return true;
+ }
+ break;
+ case Brotli:
+ case Zstandard:
+ // This value is mentioned in docs for
+ // QNetworkRequest::setMinimumArchiveBombSize, keep synchronized
+ if (ratio > 100) {
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Returns true if there are encoded bytes left or there is some
+ indication that the decoder still has data left internally.
+
+ \note Even if this returns true the next call to read() might
+ read 0 bytes. This most likely means the decompression is done.
+*/
+bool QDecompressHelper::hasData() const
+{
+ return hasDataInternal() || !decompressedDataBuffer.isEmpty();
+}
+
+/*!
+ \internal
+ Like hasData() but internally the buffer of decompressed data is
+ not interesting.
+*/
+bool QDecompressHelper::hasDataInternal() const
+{
+ return encodedBytesAvailable() || decoderHasData;
+}
+
+qint64 QDecompressHelper::encodedBytesAvailable() const
+{
+ return compressedDataBuffer.byteAmount();
+}
+
+/*!
+ \internal
+ Returns whether or not the object is valid.
+ If it becomes invalid after an operation has been performed
+ then an error has occurred.
+ \sa errorString()
+*/
+bool QDecompressHelper::isValid() const
+{
+ return contentEncoding != None;
+}
+
+/*!
+ \internal
+ Returns a string describing the error that occurred or an empty
+ string if no error occurred.
+ \sa isValid()
+*/
+QString QDecompressHelper::errorString() const
+{
+ return errorStr;
+}
+
+void QDecompressHelper::clear()
+{
+ switch (contentEncoding) {
+ case None:
+ break;
+ case Deflate:
+ case GZip: {
+ z_stream *inflateStream = toZlibPointer(decoderPointer);
+ if (inflateStream)
+ inflateEnd(inflateStream);
+ delete inflateStream;
+ break;
+ }
+ case Brotli: {
+#if QT_CONFIG(brotli)
+ BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
+ if (brotliDecoderState)
+ BrotliDecoderDestroyInstance(brotliDecoderState);
+#endif
+ break;
+ }
+ case Zstandard: {
+#if QT_CONFIG(zstd)
+ ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
+ if (zstdStream)
+ ZSTD_freeDStream(zstdStream);
+#endif
+ break;
+ }
+ }
+ decoderPointer = nullptr;
+ contentEncoding = None;
+
+ compressedDataBuffer.clear();
+ decompressedDataBuffer.clear();
+ decoderHasData = false;
+
+ countDecompressed = false;
+ countHelper.reset();
+ totalBytesRead = 0;
+ totalUncompressedBytes = 0;
+ totalCompressedBytes = 0;
+
+ errorStr.clear();
+}
+
+qsizetype QDecompressHelper::readZLib(char *data, const qsizetype maxSize)
+{
+ bool triedRawDeflate = false;
+
+ z_stream *inflateStream = toZlibPointer(decoderPointer);
+ static const size_t zlibMaxSize =
+ size_t(std::numeric_limits<decltype(inflateStream->avail_in)>::max());
+
+ QByteArrayView input = compressedDataBuffer.readPointer();
+ if (size_t(input.size()) > zlibMaxSize)
+ input = input.sliced(zlibMaxSize);
+
+ inflateStream->avail_in = input.size();
+ inflateStream->next_in = reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
+
+ bool bigMaxSize = (zlibMaxSize < size_t(maxSize));
+ qsizetype adjustedAvailableOut = bigMaxSize ? qsizetype(zlibMaxSize) : maxSize;
+ inflateStream->avail_out = adjustedAvailableOut;
+ inflateStream->next_out = reinterpret_cast<Bytef *>(data);
+
+ qsizetype bytesDecoded = 0;
+ do {
+ auto previous_avail_out = inflateStream->avail_out;
+ int ret = inflate(inflateStream, Z_NO_FLUSH);
+ // All negative return codes are errors, in the context of HTTP compression, Z_NEED_DICT is
+ // also an error.
+ // in the case where we get Z_DATA_ERROR this could be because we received raw deflate
+ // compressed data.
+ if (ret == Z_DATA_ERROR && !triedRawDeflate) {
+ inflateEnd(inflateStream);
+ triedRawDeflate = true;
+ inflateStream->zalloc = Z_NULL;
+ inflateStream->zfree = Z_NULL;
+ inflateStream->opaque = Z_NULL;
+ inflateStream->avail_in = 0;
+ inflateStream->next_in = Z_NULL;
+ int ret = inflateInit2(inflateStream, -MAX_WBITS);
+ if (ret != Z_OK) {
+ return -1;
+ } else {
+ inflateStream->avail_in = input.size();
+ inflateStream->next_in =
+ reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
+ continue;
+ }
+ } else if (ret < 0 || ret == Z_NEED_DICT) {
+ return -1;
+ }
+ bytesDecoded += qsizetype(previous_avail_out - inflateStream->avail_out);
+ if (ret == Z_STREAM_END) {
+
+ // If there's more data after the stream then this is probably composed of multiple
+ // streams.
+ if (inflateStream->avail_in != 0) {
+ inflateEnd(inflateStream);
+ Bytef *next_in = inflateStream->next_in;
+ uInt avail_in = inflateStream->avail_in;
+ inflateStream->zalloc = Z_NULL;
+ inflateStream->zfree = Z_NULL;
+ inflateStream->opaque = Z_NULL;
+ if (inflateInit2(inflateStream, MAX_WBITS + 32) != Z_OK) {
+ delete inflateStream;
+ decoderPointer = nullptr;
+ // Failed to reinitialize, so we'll just return what we have
+ compressedDataBuffer.advanceReadPointer(input.size() - avail_in);
+ return bytesDecoded;
+ } else {
+ inflateStream->next_in = next_in;
+ inflateStream->avail_in = avail_in;
+ // Keep going to handle the other cases below
+ }
+ } else {
+ // No extra data, stream is at the end. We're done.
+ compressedDataBuffer.advanceReadPointer(input.size());
+ return bytesDecoded;
+ }
+ }
+
+ if (bigMaxSize && inflateStream->avail_out == 0) {
+ // Need to adjust the next_out and avail_out parameters since we reached the end
+ // of the current range
+ bigMaxSize = (zlibMaxSize < size_t(maxSize - bytesDecoded));
+ inflateStream->avail_out = bigMaxSize ? qsizetype(zlibMaxSize) : maxSize - bytesDecoded;
+ inflateStream->next_out = reinterpret_cast<Bytef *>(data + bytesDecoded);
+ }
+
+ if (inflateStream->avail_in == 0 && inflateStream->avail_out > 0) {
+ // Grab the next input!
+ compressedDataBuffer.advanceReadPointer(input.size());
+ input = compressedDataBuffer.readPointer();
+ if (size_t(input.size()) > zlibMaxSize)
+ input = input.sliced(zlibMaxSize);
+ inflateStream->avail_in = input.size();
+ inflateStream->next_in =
+ reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
+ }
+ } while (inflateStream->avail_out > 0 && inflateStream->avail_in > 0);
+
+ compressedDataBuffer.advanceReadPointer(input.size() - inflateStream->avail_in);
+
+ return bytesDecoded;
+}
+
+qsizetype QDecompressHelper::readBrotli(char *data, const qsizetype maxSize)
+{
+#if !QT_CONFIG(brotli)
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ Q_UNREACHABLE();
+#else
+ qint64 bytesDecoded = 0;
+
+ BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
+
+ while (decoderHasData && bytesDecoded < maxSize) {
+ Q_ASSERT(brotliUnconsumedDataPtr || BrotliDecoderHasMoreOutput(brotliDecoderState));
+ if (brotliUnconsumedDataPtr) {
+ Q_ASSERT(brotliUnconsumedAmount);
+ size_t toRead = std::min(size_t(maxSize - bytesDecoded), brotliUnconsumedAmount);
+ memcpy(data + bytesDecoded, brotliUnconsumedDataPtr, toRead);
+ bytesDecoded += toRead;
+ brotliUnconsumedAmount -= toRead;
+ brotliUnconsumedDataPtr += toRead;
+ if (brotliUnconsumedAmount == 0) {
+ brotliUnconsumedDataPtr = nullptr;
+ decoderHasData = false;
+ }
+ }
+ if (BrotliDecoderHasMoreOutput(brotliDecoderState) == BROTLI_TRUE) {
+ brotliUnconsumedDataPtr =
+ BrotliDecoderTakeOutput(brotliDecoderState, &brotliUnconsumedAmount);
+ decoderHasData = true;
+ }
+ }
+ if (bytesDecoded == maxSize)
+ return bytesDecoded;
+ Q_ASSERT(bytesDecoded < maxSize);
+
+ QByteArrayView input = compressedDataBuffer.readPointer();
+ const uint8_t *encodedPtr = reinterpret_cast<const uint8_t *>(input.data());
+ size_t encodedBytesRemaining = input.size();
+
+ uint8_t *decodedPtr = reinterpret_cast<uint8_t *>(data + bytesDecoded);
+ size_t unusedDecodedSize = size_t(maxSize - bytesDecoded);
+ while (unusedDecodedSize > 0) {
+ auto previousUnusedDecodedSize = unusedDecodedSize;
+ BrotliDecoderResult result = BrotliDecoderDecompressStream(
+ brotliDecoderState, &encodedBytesRemaining, &encodedPtr, &unusedDecodedSize,
+ &decodedPtr, nullptr);
+ bytesDecoded += previousUnusedDecodedSize - unusedDecodedSize;
+
+ switch (result) {
+ case BROTLI_DECODER_RESULT_ERROR:
+ errorStr = QLatin1String("Brotli error: %1")
+ .arg(QString::fromUtf8(BrotliDecoderErrorString(
+ BrotliDecoderGetErrorCode(brotliDecoderState))));
+ return -1;
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ BrotliDecoderDestroyInstance(brotliDecoderState);
+ decoderPointer = nullptr;
+ compressedDataBuffer.clear();
+ return bytesDecoded;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ compressedDataBuffer.advanceReadPointer(input.size());
+ input = compressedDataBuffer.readPointer();
+ if (!input.isEmpty()) {
+ encodedPtr = reinterpret_cast<const uint8_t *>(input.constData());
+ encodedBytesRemaining = input.size();
+ break;
+ }
+ return bytesDecoded;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ // Some data is leftover inside the brotli decoder, remember for next time
+ decoderHasData = BrotliDecoderHasMoreOutput(brotliDecoderState);
+ Q_ASSERT(unusedDecodedSize == 0);
+ break;
+ }
+ }
+ compressedDataBuffer.advanceReadPointer(input.size() - encodedBytesRemaining);
+ return bytesDecoded;
+#endif
+}
+
+qsizetype QDecompressHelper::readZstandard(char *data, const qsizetype maxSize)
+{
+#if !QT_CONFIG(zstd)
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ Q_UNREACHABLE();
+#else
+ ZSTD_DStream *zstdStream = toZstandardPointer(decoderPointer);
+
+ QByteArrayView input = compressedDataBuffer.readPointer();
+ ZSTD_inBuffer inBuf { input.data(), size_t(input.size()), 0 };
+
+ ZSTD_outBuffer outBuf { data, size_t(maxSize), 0 };
+
+ qsizetype bytesDecoded = 0;
+ while (outBuf.pos < outBuf.size && (inBuf.pos < inBuf.size || decoderHasData)) {
+ size_t retValue = ZSTD_decompressStream(zstdStream, &outBuf, &inBuf);
+ if (ZSTD_isError(retValue)) {
+ errorStr = QLatin1String("ZStandard error: %1")
+ .arg(QString::fromUtf8(ZSTD_getErrorName(retValue)));
+ return -1;
+ } else {
+ decoderHasData = false;
+ bytesDecoded = outBuf.pos;
+ // if pos == size then there may be data left over in internal buffers
+ if (outBuf.pos == outBuf.size) {
+ decoderHasData = true;
+ } else if (inBuf.pos == inBuf.size) {
+ compressedDataBuffer.advanceReadPointer(input.size());
+ input = compressedDataBuffer.readPointer();
+ inBuf = { input.constData(), size_t(input.size()), 0 };
+ }
+ }
+ }
+ compressedDataBuffer.advanceReadPointer(inBuf.pos);
+ return bytesDecoded;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qdecompresshelper_p.h b/src/network/access/qdecompresshelper_p.h
new file mode 100644
index 0000000000..c837c14521
--- /dev/null
+++ b/src/network/access/qdecompresshelper_p.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef DECOMPRESS_HELPER_P_H
+#define DECOMPRESS_HELPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtCore/private/qbytedata_p.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+class Q_AUTOTEST_EXPORT QDecompressHelper
+{
+public:
+ enum ContentEncoding {
+ None,
+ Deflate,
+ GZip,
+ Brotli,
+ Zstandard,
+ };
+
+ QDecompressHelper() = default;
+ ~QDecompressHelper();
+
+ bool setEncoding(QByteArrayView contentEncoding);
+
+ bool isCountingBytes() const;
+ void setCountingBytesEnabled(bool shouldCount);
+
+ qint64 uncompressedSize() const;
+
+ bool hasData() const;
+ void feed(const QByteArray &data);
+ void feed(QByteArray &&data);
+ void feed(const QByteDataBuffer &buffer);
+ void feed(QByteDataBuffer &&buffer);
+ qsizetype read(char *data, qsizetype maxSize);
+
+ bool isValid() const;
+
+ void clear();
+
+ void setDecompressedSafetyCheckThreshold(qint64 threshold);
+
+ static bool isSupportedEncoding(QByteArrayView encoding);
+ static QByteArrayList acceptedEncoding();
+
+ QString errorString() const;
+
+private:
+ bool isPotentialArchiveBomb() const;
+ bool hasDataInternal() const;
+ qsizetype readInternal(char *data, qsizetype maxSize);
+
+ bool countInternal();
+ bool countInternal(const QByteArray &data);
+ bool countInternal(const QByteDataBuffer &buffer);
+
+ bool setEncoding(ContentEncoding ce);
+ qint64 encodedBytesAvailable() const;
+
+ qsizetype readZLib(char *data, qsizetype maxSize);
+ qsizetype readBrotli(char *data, qsizetype maxSize);
+ qsizetype readZstandard(char *data, qsizetype maxSize);
+
+ QByteDataBuffer compressedDataBuffer;
+ QByteDataBuffer decompressedDataBuffer;
+ const qsizetype MaxDecompressedDataBufferSize = 10 * 1024 * 1024;
+ bool decoderHasData = false;
+
+ bool countDecompressed = false;
+ std::unique_ptr<QDecompressHelper> countHelper;
+
+ QString errorStr;
+
+ // Used for calculating the ratio
+ qint64 archiveBombCheckThreshold = 10 * 1024 * 1024;
+ qint64 totalUncompressedBytes = 0;
+ qint64 totalCompressedBytes = 0;
+ qint64 totalBytesRead = 0;
+
+ ContentEncoding contentEncoding = None;
+
+ void *decoderPointer = nullptr;
+#if QT_CONFIG(brotli)
+ const uint8_t *brotliUnconsumedDataPtr = nullptr;
+ size_t brotliUnconsumedAmount = 0;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // DECOMPRESS_HELPER_P_H
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
deleted file mode 100644
index 62ae1adbd9..0000000000
--- a/src/network/access/qftp.cpp
+++ /dev/null
@@ -1,2470 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//#define QFTPPI_DEBUG
-//#define QFTPDTP_DEBUG
-
-#include "private/qftp_p.h"
-#include "qabstractsocket.h"
-
-#include "qcoreapplication.h"
-#include "qtcpsocket.h"
-#include "qurlinfo_p.h"
-#include "qstringlist.h"
-#include "qregexp.h"
-#include "qtimer.h"
-#include "qfileinfo.h"
-#include "qtcpserver.h"
-#include "qlocale.h"
-
-QT_BEGIN_NAMESPACE
-
-class QFtpPI;
-
-/*
- The QFtpDTP (DTP = Data Transfer Process) controls all client side
- data transfer between the client and server.
-*/
-class QFtpDTP : public QObject
-{
- Q_OBJECT
-
-public:
- enum ConnectState {
- CsHostFound,
- CsConnected,
- CsClosed,
- CsHostNotFound,
- CsConnectionRefused
- };
-
- QFtpDTP(QFtpPI *p, QObject *parent = nullptr);
-
- void setData(QByteArray *);
- void setDevice(QIODevice *);
- void writeData();
- void setBytesTotal(qint64 bytes);
-
- bool hasError() const;
- QString errorMessage() const;
- void clearError();
-
- void connectToHost(const QString & host, quint16 port);
- int setupListener(const QHostAddress &address);
- void waitForConnection();
-
- QTcpSocket::SocketState state() const;
- qint64 bytesAvailable() const;
- qint64 read(char *data, qint64 maxlen);
- QByteArray readAll();
-
- void abortConnection();
-
- static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);
-
-signals:
- void listInfo(const QUrlInfo&);
- void readyRead();
- void dataTransferProgress(qint64, qint64);
-
- void connectState(int);
-
-private slots:
- void socketConnected();
- void socketReadyRead();
- void socketError(QAbstractSocket::SocketError);
- void socketConnectionClosed();
- void socketBytesWritten(qint64);
- void setupSocket();
-
- void dataReadyRead();
-
-private:
- void clearData();
-
- QTcpSocket *socket;
- QTcpServer listener;
-
- QFtpPI *pi;
- QString err;
- qint64 bytesDone;
- qint64 bytesTotal;
- bool callWriteData;
-
- // If is_ba is true, ba is used; ba is never 0.
- // Otherwise dev is used; dev can be 0 or not.
- union {
- QByteArray *ba;
- QIODevice *dev;
- } data;
- bool is_ba;
-
- QByteArray bytesFromSocket;
-};
-
-/**********************************************************************
- *
- * QFtpPI - Protocol Interpreter
- *
- *********************************************************************/
-
-class QFtpPI : public QObject
-{
- Q_OBJECT
-
-public:
- QFtpPI(QObject *parent = nullptr);
-
- void connectToHost(const QString &host, quint16 port);
-
- bool sendCommands(const QStringList &cmds);
- bool sendCommand(const QString &cmd)
- { return sendCommands(QStringList(cmd)); }
-
- void clearPendingCommands();
- void abort();
-
- QString currentCommand() const
- { return currentCmd; }
-
- bool rawCommand;
- bool transferConnectionExtended;
-
- QFtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
- // makes the design simpler this way
-signals:
- void connectState(int);
- void finished(const QString&);
- void error(int, const QString&);
- void rawFtpReply(int, const QString&);
-
-private slots:
- void hostFound();
- void connected();
- void connectionClosed();
- void delayedCloseFinished();
- void readyRead();
- void error(QAbstractSocket::SocketError);
-
- void dtpConnectState(int);
-
-private:
- // the states are modelled after the generalized state diagram of RFC 959,
- // page 58
- enum State {
- Begin,
- Idle,
- Waiting,
- Success,
- Failure
- };
-
- enum AbortState {
- None,
- AbortStarted,
- WaitForAbortToFinish
- };
-
- bool processReply();
- bool startNextCmd();
-
- QTcpSocket commandSocket;
- QString replyText;
- char replyCode[3];
- State state;
- AbortState abortState;
- QStringList pendingCommands;
- QString currentCmd;
-
- bool waitForDtpToConnect;
- bool waitForDtpToClose;
-
- QByteArray bytesFromSocket;
-
- friend class QFtpDTP;
-};
-
-/**********************************************************************
- *
- * QFtpCommand implemenatation
- *
- *********************************************************************/
-class QFtpCommand
-{
-public:
- QFtpCommand(QFtp::Command cmd, const QStringList &raw, const QByteArray &ba);
- QFtpCommand(QFtp::Command cmd, const QStringList &raw, QIODevice *dev = nullptr);
- ~QFtpCommand();
-
- int id;
- QFtp::Command command;
- QStringList rawCmds;
-
- // If is_ba is true, ba is used; ba is never 0.
- // Otherwise dev is used; dev can be 0 or not.
- union {
- QByteArray *ba;
- QIODevice *dev;
- } data;
- bool is_ba;
-
-};
-
-static int nextId()
-{
- static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
- return 1 + counter.fetchAndAddRelaxed(1);
-}
-
-QFtpCommand::QFtpCommand(QFtp::Command cmd, const QStringList &raw, const QByteArray &ba)
- : command(cmd), rawCmds(raw), is_ba(true)
-{
- id = nextId();
- data.ba = new QByteArray(ba);
-}
-
-QFtpCommand::QFtpCommand(QFtp::Command cmd, const QStringList &raw, QIODevice *dev)
- : command(cmd), rawCmds(raw), is_ba(false)
-{
- id = nextId();
- data.dev = dev;
-}
-
-QFtpCommand::~QFtpCommand()
-{
- if (is_ba)
- delete data.ba;
-}
-
-/**********************************************************************
- *
- * QFtpDTP implemenatation
- *
- *********************************************************************/
-QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) :
- QObject(parent),
- socket(nullptr),
- listener(this),
- pi(p),
- callWriteData(false)
-{
- clearData();
- listener.setObjectName(QLatin1String("QFtpDTP active state server"));
- connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket()));
-}
-
-void QFtpDTP::setData(QByteArray *ba)
-{
- is_ba = true;
- data.ba = ba;
-}
-
-void QFtpDTP::setDevice(QIODevice *dev)
-{
- is_ba = false;
- data.dev = dev;
-}
-
-void QFtpDTP::setBytesTotal(qint64 bytes)
-{
- bytesTotal = bytes;
- bytesDone = 0;
- emit dataTransferProgress(bytesDone, bytesTotal);
-}
-
-void QFtpDTP::connectToHost(const QString & host, quint16 port)
-{
- bytesFromSocket.clear();
-
- if (socket) {
- delete socket;
- socket = nullptr;
- }
- socket = new QTcpSocket(this);
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket
- socket->setProperty("_q_networksession", property("_q_networksession"));
-#endif
- socket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));
- connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
- connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
- connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
- connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
- connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
-
- socket->connectToHost(host, port);
-}
-
-int QFtpDTP::setupListener(const QHostAddress &address)
-{
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket
- listener.setProperty("_q_networksession", property("_q_networksession"));
-#endif
- if (!listener.isListening() && !listener.listen(address, 0))
- return -1;
- return listener.serverPort();
-}
-
-void QFtpDTP::waitForConnection()
-{
- // This function is only interesting in Active transfer mode; it works
- // around a limitation in QFtp's design by blocking, waiting for an
- // incoming connection. For the default Passive mode, it does nothing.
- if (listener.isListening())
- listener.waitForNewConnection();
-}
-
-QTcpSocket::SocketState QFtpDTP::state() const
-{
- return socket ? socket->state() : QTcpSocket::UnconnectedState;
-}
-
-qint64 QFtpDTP::bytesAvailable() const
-{
- if (!socket || socket->state() != QTcpSocket::ConnectedState)
- return (qint64) bytesFromSocket.size();
- return socket->bytesAvailable();
-}
-
-qint64 QFtpDTP::read(char *data, qint64 maxlen)
-{
- qint64 read;
- if (socket && socket->state() == QTcpSocket::ConnectedState) {
- read = socket->read(data, maxlen);
- } else {
- read = qMin(maxlen, qint64(bytesFromSocket.size()));
- memcpy(data, bytesFromSocket.data(), read);
- bytesFromSocket.remove(0, read);
- }
-
- bytesDone += read;
- return read;
-}
-
-QByteArray QFtpDTP::readAll()
-{
- QByteArray tmp;
- if (socket && socket->state() == QTcpSocket::ConnectedState) {
- tmp = socket->readAll();
- bytesDone += tmp.size();
- } else {
- tmp = bytesFromSocket;
- bytesFromSocket.clear();
- }
- return tmp;
-}
-
-void QFtpDTP::writeData()
-{
- if (!socket)
- return;
-
- if (is_ba) {
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size());
-#endif
- if (data.ba->size() == 0)
- emit dataTransferProgress(0, bytesTotal);
- else
- socket->write(data.ba->data(), data.ba->size());
-
- socket->close();
-
- clearData();
- } else if (data.dev) {
- callWriteData = false;
- const qint64 blockSize = 16*1024;
- char buf[16*1024];
- qint64 read = data.dev->read(buf, blockSize);
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::writeData: write() of size %lli bytes", read);
-#endif
- if (read > 0) {
- socket->write(buf, read);
- } else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {
- // error or EOF
- if (bytesDone == 0 && socket->bytesToWrite() == 0)
- emit dataTransferProgress(0, bytesTotal);
- socket->close();
- clearData();
- }
-
- // do we continue uploading?
- callWriteData = data.dev != nullptr;
- }
-}
-
-void QFtpDTP::dataReadyRead()
-{
- writeData();
-}
-
-inline bool QFtpDTP::hasError() const
-{
- return !err.isNull();
-}
-
-inline QString QFtpDTP::errorMessage() const
-{
- return err;
-}
-
-inline void QFtpDTP::clearError()
-{
- err.clear();
-}
-
-void QFtpDTP::abortConnection()
-{
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",
- socket ? socket->bytesAvailable() : (qint64) 0);
-#endif
- callWriteData = false;
- clearData();
-
- if (socket)
- socket->abort();
-}
-
-static void _q_fixupDateTime(QDateTime *dateTime)
-{
- // Adjust for future tolerance.
- const int futureTolerance = 86400;
- if (dateTime->secsTo(QDateTime::currentDateTime()) < -futureTolerance) {
- QDate d = dateTime->date();
- d.setDate(d.year() - 1, d.month(), d.day());
- dateTime->setDate(d);
- }
-}
-
-static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
-{
- // Unix style, 7 + 1 entries
- // -rw-r--r-- 1 ftp ftp 17358091 Aug 10 2004 qt-x11-free-3.3.3.tar.gz
- // drwxr-xr-x 3 ftp ftp 4096 Apr 14 2000 compiled-examples
- // lrwxrwxrwx 1 ftp ftp 9 Oct 29 2005 qtscape -> qtmozilla
- if (tokens.size() != 8)
- return;
-
- char first = tokens.at(1).at(0).toLatin1();
- if (first == 'd') {
- info->setDir(true);
- info->setFile(false);
- info->setSymLink(false);
- } else if (first == '-') {
- info->setDir(false);
- info->setFile(true);
- info->setSymLink(false);
- } else if (first == 'l') {
- info->setDir(true);
- info->setFile(false);
- info->setSymLink(true);
- }
-
- // Resolve filename
- QString name = tokens.at(7);
- if (info->isSymLink()) {
- int linkPos = name.indexOf(QLatin1String(" ->"));
- if (linkPos != -1)
- name.resize(linkPos);
- }
- info->setName(name);
-
- // Resolve owner & group
- info->setOwner(tokens.at(3));
- info->setGroup(tokens.at(4));
-
- // Resolve size
- info->setSize(tokens.at(5).toLongLong());
-
- QStringList formats;
- formats << QLatin1String("MMM dd yyyy") << QLatin1String("MMM dd hh:mm") << QLatin1String("MMM d yyyy")
- << QLatin1String("MMM d hh:mm") << QLatin1String("MMM d yyyy") << QLatin1String("MMM dd yyyy");
-
- QString dateString = tokens.at(6);
- dateString[0] = dateString[0].toUpper();
-
- // Resolve the modification date by parsing all possible formats
- QDateTime dateTime;
- int n = 0;
-#if QT_CONFIG(datestring)
- do {
- dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));
- } while (n < formats.size() && (!dateTime.isValid()));
-#endif
-
- if (n == 2 || n == 4) {
- // Guess the year.
- dateTime.setDate(QDate(QDate::currentDate().year(),
- dateTime.date().month(),
- dateTime.date().day()));
- _q_fixupDateTime(&dateTime);
- }
- if (dateTime.isValid())
- info->setLastModified(dateTime);
-
- // Resolve permissions
- int permissions = 0;
- const QString &p = tokens.at(2);
- permissions |= (p[0] == QLatin1Char('r') ? QUrlInfo::ReadOwner : 0);
- permissions |= (p[1] == QLatin1Char('w') ? QUrlInfo::WriteOwner : 0);
- permissions |= (p[2] == QLatin1Char('x') ? QUrlInfo::ExeOwner : 0);
- permissions |= (p[3] == QLatin1Char('r') ? QUrlInfo::ReadGroup : 0);
- permissions |= (p[4] == QLatin1Char('w') ? QUrlInfo::WriteGroup : 0);
- permissions |= (p[5] == QLatin1Char('x') ? QUrlInfo::ExeGroup : 0);
- permissions |= (p[6] == QLatin1Char('r') ? QUrlInfo::ReadOther : 0);
- permissions |= (p[7] == QLatin1Char('w') ? QUrlInfo::WriteOther : 0);
- permissions |= (p[8] == QLatin1Char('x') ? QUrlInfo::ExeOther : 0);
- info->setPermissions(permissions);
-
- bool isOwner = info->owner() == userName;
- info->setReadable((permissions & QUrlInfo::ReadOther) || ((permissions & QUrlInfo::ReadOwner) && isOwner));
- info->setWritable((permissions & QUrlInfo::WriteOther) || ((permissions & QUrlInfo::WriteOwner) && isOwner));
-}
-
-static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
-{
- // DOS style, 3 + 1 entries
- // 01-16-02 11:14AM <DIR> epsgroup
- // 06-05-03 03:19PM 1973 readme.txt
- if (tokens.size() != 4)
- return;
-
- Q_UNUSED(userName);
-
- QString name = tokens.at(3);
- info->setName(name);
- info->setSymLink(name.endsWith(QLatin1String(".lnk"), Qt::CaseInsensitive));
-
- if (tokens.at(2) == QLatin1String("<DIR>")) {
- info->setFile(false);
- info->setDir(true);
- } else {
- info->setFile(true);
- info->setDir(false);
- info->setSize(tokens.at(2).toLongLong());
- }
-
- // Note: We cannot use QFileInfo; permissions are for the server-side
- // machine, and QFileInfo's behavior depends on the local platform.
- int permissions = QUrlInfo::ReadOwner | QUrlInfo::WriteOwner
- | QUrlInfo::ReadGroup | QUrlInfo::WriteGroup
- | QUrlInfo::ReadOther | QUrlInfo::WriteOther;
- QStringRef ext;
- int extIndex = name.lastIndexOf(QLatin1Char('.'));
- if (extIndex != -1)
- ext = name.midRef(extIndex + 1);
- if (ext == QLatin1String("exe") || ext == QLatin1String("bat") || ext == QLatin1String("com"))
- permissions |= QUrlInfo::ExeOwner | QUrlInfo::ExeGroup | QUrlInfo::ExeOther;
- info->setPermissions(permissions);
-
- info->setReadable(true);
- info->setWritable(info->isFile());
-
- QDateTime dateTime;
-#if QT_CONFIG(datestring)
- dateTime = QLocale::c().toDateTime(tokens.at(1), QLatin1String("MM-dd-yy hh:mmAP"));
- if (dateTime.date().year() < 1971) {
- dateTime.setDate(QDate(dateTime.date().year() + 100,
- dateTime.date().month(),
- dateTime.date().day()));
- }
-#endif
-
- info->setLastModified(dateTime);
-
-}
-
-bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info)
-{
- if (buffer.isEmpty())
- return false;
-
- QString bufferStr = QString::fromUtf8(buffer).trimmed();
-
- // Unix style FTP servers
- QRegExp unixPattern(QLatin1String("^([\\-dl])([a-zA-Z\\-]{9,9})\\s+\\d+\\s+(\\S*)\\s+"
- "(\\S*)\\s+(\\d+)\\s+(\\S+\\s+\\S+\\s+\\S+)\\s+(\\S.*)"));
- if (unixPattern.indexIn(bufferStr) == 0) {
- _q_parseUnixDir(unixPattern.capturedTexts(), userName, info);
- return true;
- }
-
- // DOS style FTP servers
- QRegExp dosPattern(QLatin1String("^(\\d\\d-\\d\\d-\\d\\d\\ \\ \\d\\d:\\d\\d[AP]M)\\s+"
- "(<DIR>|\\d+)\\s+(\\S.*)$"));
- if (dosPattern.indexIn(bufferStr) == 0) {
- _q_parseDosDir(dosPattern.capturedTexts(), userName, info);
- return true;
- }
-
- // Unsupported
- return false;
-}
-
-void QFtpDTP::socketConnected()
-{
- bytesDone = 0;
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::connectState(CsConnected)");
-#endif
- emit connectState(QFtpDTP::CsConnected);
-}
-
-void QFtpDTP::socketReadyRead()
-{
- if (!socket)
- return;
-
- if (pi->currentCommand().isEmpty()) {
- socket->close();
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::connectState(CsClosed)");
-#endif
- emit connectState(QFtpDTP::CsClosed);
- return;
- }
-
- if (pi->abortState != QFtpPI::None) {
- // discard data
- socket->readAll();
- return;
- }
-
- if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {
- while (socket->canReadLine()) {
- QUrlInfo i;
- QByteArray line = socket->readLine();
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP read (list): '%s'", line.constData());
-#endif
- if (parseDir(line, QLatin1String(""), &i)) {
- emit listInfo(i);
- } else {
- // some FTP servers don't return a 550 if the file or directory
- // does not exist, but rather write a text to the data socket
- // -- try to catch these cases
- if (line.endsWith("No such file or directory\r\n"))
- err = QString::fromUtf8(line);
- }
- }
- } else {
- if (!is_ba && data.dev) {
- do {
- QByteArray ba;
- ba.resize(socket->bytesAvailable());
- qint64 bytesRead = socket->read(ba.data(), ba.size());
- if (bytesRead < 0) {
- // a read following a readyRead() signal will
- // never fail.
- return;
- }
- ba.resize(bytesRead);
- bytesDone += bytesRead;
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone);
-#endif
- if (data.dev) // make sure it wasn't deleted in the slot
- data.dev->write(ba);
- emit dataTransferProgress(bytesDone, bytesTotal);
-
- // Need to loop; dataTransferProgress is often connected to
- // slots that update the GUI (e.g., progress bar values), and
- // if events are processed, more data may have arrived.
- } while (socket->bytesAvailable());
- } else {
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",
- bytesAvailable(), bytesDone);
-#endif
- emit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);
- emit readyRead();
- }
- }
-}
-
-void QFtpDTP::socketError(QAbstractSocket::SocketError e)
-{
- if (e == QTcpSocket::HostNotFoundError) {
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::connectState(CsHostNotFound)");
-#endif
- emit connectState(QFtpDTP::CsHostNotFound);
- } else if (e == QTcpSocket::ConnectionRefusedError) {
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::connectState(CsConnectionRefused)");
-#endif
- emit connectState(QFtpDTP::CsConnectionRefused);
- }
-}
-
-void QFtpDTP::socketConnectionClosed()
-{
- if (!is_ba && data.dev) {
- clearData();
- }
-
- if (socket->isOpen())
- bytesFromSocket = socket->readAll();
- else
- bytesFromSocket.clear();
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::connectState(CsClosed)");
-#endif
- emit connectState(QFtpDTP::CsClosed);
-}
-
-void QFtpDTP::socketBytesWritten(qint64 bytes)
-{
- bytesDone += bytes;
-#if defined(QFTPDTP_DEBUG)
- qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone);
-#endif
- emit dataTransferProgress(bytesDone, bytesTotal);
- if (callWriteData)
- writeData();
-}
-
-void QFtpDTP::setupSocket()
-{
- socket = listener.nextPendingConnection();
- socket->setObjectName(QLatin1String("QFtpDTP Active state socket"));
- connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
- connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
- connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
- connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
- connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
-
- listener.close();
-}
-
-void QFtpDTP::clearData()
-{
- is_ba = false;
- data.dev = nullptr;
-}
-
-/**********************************************************************
- *
- * QFtpPI implemenatation
- *
- *********************************************************************/
-QFtpPI::QFtpPI(QObject *parent) :
- QObject(parent),
- rawCommand(false),
- transferConnectionExtended(true),
- dtp(this),
- commandSocket(nullptr),
- state(Begin), abortState(None),
- currentCmd(QString()),
- waitForDtpToConnect(false),
- waitForDtpToClose(false)
-{
- commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));
- connect(&commandSocket, SIGNAL(hostFound()),
- SLOT(hostFound()));
- connect(&commandSocket, SIGNAL(connected()),
- SLOT(connected()));
- connect(&commandSocket, SIGNAL(disconnected()),
- SLOT(connectionClosed()));
- connect(&commandSocket, SIGNAL(readyRead()),
- SLOT(readyRead()));
- connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),
- SLOT(error(QAbstractSocket::SocketError)));
-
- connect(&dtp, SIGNAL(connectState(int)),
- SLOT(dtpConnectState(int)));
-}
-
-void QFtpPI::connectToHost(const QString &host, quint16 port)
-{
- emit connectState(QFtp::HostLookup);
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket & DTP
- commandSocket.setProperty("_q_networksession", property("_q_networksession"));
- dtp.setProperty("_q_networksession", property("_q_networksession"));
-#endif
- commandSocket.connectToHost(host, port);
-}
-
-/*
- \internal
-
- Sends the sequence of commands \a cmds to the FTP server. When the commands
- are all done the finished() signal is emitted. When an error occurs, the
- error() signal is emitted.
-
- If there are pending commands in the queue this functions returns \c false and
- the \a cmds are not added to the queue; otherwise it returns \c true.
-*/
-bool QFtpPI::sendCommands(const QStringList &cmds)
-{
- if (!pendingCommands.isEmpty())
- return false;
-
- if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {
- emit error(QFtp::NotConnected, QFtp::tr("Not connected"));
- return true; // there are no pending commands
- }
-
- pendingCommands = cmds;
- startNextCmd();
- return true;
-}
-
-void QFtpPI::clearPendingCommands()
-{
- pendingCommands.clear();
- dtp.abortConnection();
- currentCmd.clear();
- state = Idle;
-}
-
-void QFtpPI::abort()
-{
- pendingCommands.clear();
-
- if (abortState != None)
- // ABOR already sent
- return;
-
- if (currentCmd.isEmpty())
- return; //no command in progress
-
- if (currentCmd.startsWith(QLatin1String("STOR "))) {
- abortState = AbortStarted;
-#if defined(QFTPPI_DEBUG)
- qDebug("QFtpPI send: ABOR");
-#endif
- commandSocket.write("ABOR\r\n", 6);
-
- dtp.abortConnection();
- } else {
- //Deviation from RFC 959:
- //Most FTP servers do not support ABOR, or require the telnet
- //IP & synch sequence (TCP urgent data) which is not supported by QTcpSocket.
- //Following what most FTP clients do, just reset the data connection and wait for 426
- abortState = WaitForAbortToFinish;
- dtp.abortConnection();
- }
-}
-
-void QFtpPI::hostFound()
-{
- emit connectState(QFtp::Connecting);
-}
-
-void QFtpPI::connected()
-{
- state = Begin;
-#if defined(QFTPPI_DEBUG)
-// qDebug("QFtpPI state: %d [connected()]", state);
-#endif
- // try to improve performance by setting TCP_NODELAY
- commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
-
- emit connectState(QFtp::Connected);
-}
-
-void QFtpPI::connectionClosed()
-{
- commandSocket.close();
- emit connectState(QFtp::Unconnected);
-}
-
-void QFtpPI::delayedCloseFinished()
-{
- emit connectState(QFtp::Unconnected);
-}
-
-void QFtpPI::error(QAbstractSocket::SocketError e)
-{
- if (e == QTcpSocket::HostNotFoundError) {
- emit connectState(QFtp::Unconnected);
- emit error(QFtp::HostNotFound,
- QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));
- } else if (e == QTcpSocket::ConnectionRefusedError) {
- emit connectState(QFtp::Unconnected);
- emit error(QFtp::ConnectionRefused,
- QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));
- } else if (e == QTcpSocket::SocketTimeoutError) {
- emit connectState(QFtp::Unconnected);
- emit error(QFtp::ConnectionRefused,
- QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));
- }
-}
-
-void QFtpPI::readyRead()
-{
- if (waitForDtpToClose)
- return;
-
- while (commandSocket.canReadLine()) {
- // read line with respect to line continuation
- QString line = QString::fromUtf8(commandSocket.readLine());
- if (replyText.isEmpty()) {
- if (line.length() < 3) {
- // protocol error
- return;
- }
- const int lowerLimit[3] = {1,0,0};
- const int upperLimit[3] = {5,5,9};
- for (int i=0; i<3; i++) {
- replyCode[i] = line.at(i).digitValue();
- if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {
- // protocol error
- return;
- }
- }
- }
- const char count[4] = { char('0' + replyCode[0]), char('0' + replyCode[1]),
- char('0' + replyCode[2]), char(' ') };
- QString endOfMultiLine(QLatin1String(count, 4));
- QString lineCont(endOfMultiLine);
- lineCont[3] = QLatin1Char('-');
- QStringRef lineLeft4 = line.leftRef(4);
-
- while (lineLeft4 != endOfMultiLine) {
- if (lineLeft4 == lineCont)
- replyText += line.midRef(4); // strip 'xyz-'
- else
- replyText += line;
- if (!commandSocket.canReadLine())
- return;
- line = QString::fromUtf8(commandSocket.readLine());
- lineLeft4 = line.leftRef(4);
- }
- replyText += line.midRef(4); // strip reply code 'xyz '
- if (replyText.endsWith(QLatin1String("\r\n")))
- replyText.chop(2);
-
- if (processReply())
- replyText = QLatin1String("");
- }
-}
-
-/*
- \internal
-
- Process a reply from the FTP server.
-
- Returns \c true if the reply was processed or false if the reply has to be
- processed at a later point.
-*/
-bool QFtpPI::processReply()
-{
-#if defined(QFTPPI_DEBUG)
-// qDebug("QFtpPI state: %d [processReply() begin]", state);
- if (replyText.length() < 400)
- qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());
- else
- qDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
-#endif
-
- int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
-
- // process 226 replies ("Closing Data Connection") only when the data
- // connection is really closed to avoid short reads of the DTP
- if (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {
- if (dtp.state() != QTcpSocket::UnconnectedState) {
- waitForDtpToClose = true;
- return false;
- }
- }
-
- switch (abortState) {
- case AbortStarted:
- abortState = WaitForAbortToFinish;
- break;
- case WaitForAbortToFinish:
- abortState = None;
- return true;
- default:
- break;
- }
-
- // get new state
- static const State table[5] = {
- /* 1yz 2yz 3yz 4yz 5yz */
- Waiting, Success, Idle, Failure, Failure
- };
- switch (state) {
- case Begin:
- if (replyCode[0] == 1) {
- return true;
- } else if (replyCode[0] == 2) {
- state = Idle;
- emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));
- break;
- }
- // reply codes not starting with 1 or 2 are not handled.
- return true;
- case Waiting:
- if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)
- state = Failure;
- else
- if (replyCodeInt == 202)
- state = Failure;
- else
- state = table[replyCode[0] - 1];
- break;
- default:
- // ignore unrequested message
- return true;
- }
-#if defined(QFTPPI_DEBUG)
-// qDebug("QFtpPI state: %d [processReply() intermediate]", state);
-#endif
-
- // special actions on certain replies
- emit rawFtpReply(replyCodeInt, replyText);
- if (rawCommand) {
- rawCommand = false;
- } else if (replyCodeInt == 227) {
- // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
- // rfc959 does not define this response precisely, and gives
- // both examples where the parenthesis are used, and where
- // they are missing. We need to scan for the address and host
- // info.
- QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
- if (addrPortPattern.indexIn(replyText) == -1) {
-#if defined(QFTPPI_DEBUG)
- qDebug("QFtp: bad 227 response -- address and port information missing");
-#endif
- // this error should be reported
- } else {
- const QStringList lst = addrPortPattern.capturedTexts();
- QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
- quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();
- waitForDtpToConnect = true;
- dtp.connectToHost(host, port);
- }
- } else if (replyCodeInt == 229) {
- // 229 Extended Passive mode OK (|||10982|)
- int portPos = replyText.indexOf(QLatin1Char('('));
- if (portPos == -1) {
-#if defined(QFTPPI_DEBUG)
- qDebug("QFtp: bad 229 response -- port information missing");
-#endif
- // this error should be reported
- } else {
- ++portPos;
- QChar delimiter = replyText.at(portPos);
- const auto epsvParameters = replyText.midRef(portPos).split(delimiter);
-
- waitForDtpToConnect = true;
- dtp.connectToHost(commandSocket.peerAddress().toString(),
- epsvParameters.at(3).toInt());
- }
-
- } else if (replyCodeInt == 230) {
- if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
- pendingCommands.constFirst().startsWith(QLatin1String("PASS "))) {
- // no need to send the PASS -- we are already logged in
- pendingCommands.pop_front();
- }
- // 230 User logged in, proceed.
- emit connectState(QFtp::LoggedIn);
- } else if (replyCodeInt == 213) {
- // 213 File status.
- if (currentCmd.startsWith(QLatin1String("SIZE ")))
- dtp.setBytesTotal(replyText.simplified().toLongLong());
- } else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {
- dtp.waitForConnection();
- dtp.writeData();
- }
-
- // react on new state
- switch (state) {
- case Begin:
- // should never happen
- break;
- case Success:
- // success handling
- state = Idle;
- Q_FALLTHROUGH();
- case Idle:
- if (dtp.hasError()) {
- emit error(QFtp::UnknownError, dtp.errorMessage());
- dtp.clearError();
- }
- startNextCmd();
- break;
- case Waiting:
- // do nothing
- break;
- case Failure:
- // If the EPSV or EPRT commands fail, replace them with
- // the old PASV and PORT instead and try again.
- if (currentCmd.startsWith(QLatin1String("EPSV"))) {
- transferConnectionExtended = false;
- pendingCommands.prepend(QLatin1String("PASV\r\n"));
- } else if (currentCmd.startsWith(QLatin1String("EPRT"))) {
- transferConnectionExtended = false;
- pendingCommands.prepend(QLatin1String("PORT\r\n"));
- } else {
- emit error(QFtp::UnknownError, replyText);
- }
- if (state != Waiting) {
- state = Idle;
- startNextCmd();
- }
- break;
- }
-#if defined(QFTPPI_DEBUG)
-// qDebug("QFtpPI state: %d [processReply() end]", state);
-#endif
- return true;
-}
-
-/*
- \internal
-
- Starts next pending command. Returns \c false if there are no pending commands,
- otherwise it returns \c true.
-*/
-bool QFtpPI::startNextCmd()
-{
- if (waitForDtpToConnect)
- // don't process any new commands until we are connected
- return true;
-
-#if defined(QFTPPI_DEBUG)
- if (state != Idle)
- qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state);
-#endif
- if (pendingCommands.isEmpty()) {
- currentCmd.clear();
- emit finished(replyText);
- return false;
- }
- currentCmd = pendingCommands.constFirst();
-
- // PORT and PASV are edited in-place, depending on whether we
- // should try the extended transfer connection commands EPRT and
- // EPSV. The PORT command also triggers setting up a listener, and
- // the address/port arguments are edited in.
- QHostAddress address = commandSocket.localAddress();
- if (currentCmd.startsWith(QLatin1String("PORT"))) {
- if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {
- int port = dtp.setupListener(address);
- currentCmd = QLatin1String("EPRT |");
- currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');
- currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);
- currentCmd += QLatin1Char('|');
- } else if (address.protocol() == QTcpSocket::IPv4Protocol) {
- int port = dtp.setupListener(address);
- QString portArg;
- quint32 ip = address.toIPv4Address();
- portArg += QString::number((ip & 0xff000000) >> 24);
- portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);
- portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);
- portArg += QLatin1Char(',') + QString::number(ip & 0xff);
- portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);
- portArg += QLatin1Char(',') + QString::number(port & 0xff);
-
- currentCmd = QLatin1String("PORT ");
- currentCmd += portArg;
- } else {
- // No IPv6 connection can be set up with the PORT
- // command.
- return false;
- }
-
- currentCmd += QLatin1String("\r\n");
- } else if (currentCmd.startsWith(QLatin1String("PASV"))) {
- if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)
- currentCmd = QLatin1String("EPSV\r\n");
- }
-
- pendingCommands.pop_front();
-#if defined(QFTPPI_DEBUG)
- qDebug("QFtpPI send: %s", currentCmd.leftRef(currentCmd.length() - 2).toLatin1().constData());
-#endif
- state = Waiting;
- commandSocket.write(currentCmd.toUtf8());
- return true;
-}
-
-void QFtpPI::dtpConnectState(int s)
-{
- switch (s) {
- case QFtpDTP::CsClosed:
- if (waitForDtpToClose) {
- // there is an unprocessed reply
- if (processReply())
- replyText = QLatin1String("");
- else
- return;
- }
- waitForDtpToClose = false;
- readyRead();
- return;
- case QFtpDTP::CsConnected:
- waitForDtpToConnect = false;
- startNextCmd();
- return;
- case QFtpDTP::CsHostNotFound:
- case QFtpDTP::CsConnectionRefused:
- emit error(QFtp::ConnectionRefused,
- QFtp::tr("Data Connection refused"));
- startNextCmd();
- return;
- default:
- return;
- }
-}
-
-/**********************************************************************
- *
- * QFtpPrivate
- *
- *********************************************************************/
-
-QT_BEGIN_INCLUDE_NAMESPACE
-#include <private/qobject_p.h>
-QT_END_INCLUDE_NAMESPACE
-
-class QFtpPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QFtp)
-public:
-
- inline QFtpPrivate() : close_waitForStateChange(false), state(QFtp::Unconnected),
- transferMode(QFtp::Passive), error(QFtp::NoError)
- { }
-
- ~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }
-
- // private slots
- void _q_startNextCommand();
- void _q_piFinished(const QString&);
- void _q_piError(int, const QString&);
- void _q_piConnectState(int);
- void _q_piFtpReply(int, const QString&);
-
- int addCommand(QFtpCommand *cmd);
-
- QFtpPI pi;
- QList<QFtpCommand *> pending;
- bool close_waitForStateChange;
- QFtp::State state;
- QFtp::TransferMode transferMode;
- QFtp::Error error;
- QString errorString;
-
- QString host;
- quint16 port;
- QString proxyHost;
- quint16 proxyPort;
-};
-
-int QFtpPrivate::addCommand(QFtpCommand *cmd)
-{
- pending.append(cmd);
-
- if (pending.count() == 1) {
- // don't emit the commandStarted() signal before the ID is returned
- QTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));
- }
- return cmd->id;
-}
-
-/**********************************************************************
- *
- * QFtp implementation
- *
- *********************************************************************/
-/*!
- \internal
- \class QFtp
- \brief The QFtp class provides an implementation of the client side of FTP protocol.
-
- \ingroup network
- \inmodule QtNetwork
-
-
- This class provides a direct interface to FTP that allows you to
- have more control over the requests. However, for new
- applications, it is recommended to use QNetworkAccessManager and
- QNetworkReply, as those classes possess a simpler, yet more
- powerful API.
-
- The class works asynchronously, so there are no blocking
- functions. If an operation cannot be executed immediately, the
- function will still return straight away and the operation will be
- scheduled for later execution. The results of scheduled operations
- are reported via signals. This approach depends on the event loop
- being in operation.
-
- The operations that can be scheduled (they are called "commands"
- in the rest of the documentation) are the following:
- connectToHost(), login(), close(), list(), cd(), get(), put(),
- remove(), mkdir(), rmdir(), rename() and rawCommand().
-
- All of these commands return a unique identifier that allows you
- to keep track of the command that is currently being executed.
- When the execution of a command starts, the commandStarted()
- signal with the command's identifier is emitted. When the command
- is finished, the commandFinished() signal is emitted with the
- command's identifier and a bool that indicates whether the command
- finished with an error.
-
- In some cases, you might want to execute a sequence of commands,
- e.g. if you want to connect and login to a FTP server. This is
- simply achieved:
-
- \snippet code/src_network_access_qftp.cpp 0
-
- In this case two FTP commands have been scheduled. When the last
- scheduled command has finished, a done() signal is emitted with
- a bool argument that tells you whether the sequence finished with
- an error.
-
- If an error occurs during the execution of one of the commands in
- a sequence of commands, all the pending commands (i.e. scheduled,
- but not yet executed commands) are cleared and no signals are
- emitted for them.
-
- Some commands, e.g. list(), emit additional signals to report
- their results.
-
- Example: If you want to download the INSTALL file from the Qt
- FTP server, you would write this:
-
- \snippet code/src_network_access_qftp.cpp 1
-
- For this example the following sequence of signals is emitted
- (with small variations, depending on network traffic, etc.):
-
- \snippet code/src_network_access_qftp.cpp 2
-
- The dataTransferProgress() signal in the above example is useful
- if you want to show a \l{QProgressBar}{progress bar} to
- inform the user about the progress of the download. The
- readyRead() signal tells you that there is data ready to be read.
- The amount of data can be queried then with the bytesAvailable()
- function and it can be read with the read() or readAll()
- function.
-
- If the login fails for the above example, the signals would look
- like this:
-
- \snippet code/src_network_access_qftp.cpp 3
-
- You can then get details about the error with the error() and
- errorString() functions.
-
- For file transfer, QFtp can use both active or passive mode, and
- it uses passive file transfer mode by default; see the
- documentation for setTransferMode() for more details about this.
-
- Call setProxy() to make QFtp connect via an FTP proxy server.
-
- The functions currentId() and currentCommand() provide more
- information about the currently executing command.
-
- The functions hasPendingCommands() and clearPendingCommands()
- allow you to query and clear the list of pending commands.
-
- If you are an experienced network programmer and want to have
- complete control you can use rawCommand() to execute arbitrary FTP
- commands.
-
- \warning The current version of QFtp doesn't fully support
- non-Unix FTP servers.
-
- \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
- {FTP Example}
-*/
-
-
-/*!
- \internal
- Constructs a QFtp object with the given \a parent.
-*/
-QFtp::QFtp(QObject *parent)
- : QObject(*new QFtpPrivate, parent)
-{
- Q_D(QFtp);
- d->errorString = tr("Unknown error");
-
- connect(&d->pi, SIGNAL(connectState(int)),
- SLOT(_q_piConnectState(int)));
- connect(&d->pi, SIGNAL(finished(QString)),
- SLOT(_q_piFinished(QString)));
- connect(&d->pi, SIGNAL(error(int,QString)),
- SLOT(_q_piError(int,QString)));
- connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),
- SLOT(_q_piFtpReply(int,QString)));
-
- connect(&d->pi.dtp, SIGNAL(readyRead()),
- SIGNAL(readyRead()));
- connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),
- SIGNAL(dataTransferProgress(qint64,qint64)));
- connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
- SIGNAL(listInfo(QUrlInfo)));
-}
-
-/*!
- \internal
- \enum QFtp::State
-
- This enum defines the connection state:
-
- \value Unconnected There is no connection to the host.
- \value HostLookup A host name lookup is in progress.
- \value Connecting An attempt to connect to the host is in progress.
- \value Connected Connection to the host has been achieved.
- \value LoggedIn Connection and user login have been achieved.
- \value Closing The connection is closing down, but it is not yet
- closed. (The state will be \c Unconnected when the connection is
- closed.)
-
- \sa stateChanged(), state()
-*/
-/*!
- \internal
- \enum QFtp::TransferMode
-
- FTP works with two socket connections; one for commands and
- another for transmitting data. While the command connection is
- always initiated by the client, the second connection can be
- initiated by either the client or the server.
-
- This enum defines whether the client (Passive mode) or the server
- (Active mode) should set up the data connection.
-
- \value Passive The client connects to the server to transmit its
- data.
-
- \value Active The server connects to the client to transmit its
- data.
-*/
-/*!
- \internal
- \enum QFtp::TransferType
-
- This enum identifies the data transfer type used with get and
- put commands.
-
- \value Binary The data will be transferred in Binary mode.
-
- \value Ascii The data will be transferred in Ascii mode and new line
- characters will be converted to the local format.
-*/
-/*!
- \internal
- \enum QFtp::Error
-
- This enum identifies the error that occurred.
-
- \value NoError No error occurred.
- \value HostNotFound The host name lookup failed.
- \value ConnectionRefused The server refused the connection.
- \value NotConnected Tried to send a command, but there is no connection to
- a server.
- \value UnknownError An error other than those specified above
- occurred.
-
- \sa error()
-*/
-
-/*!
- \internal
- \enum QFtp::Command
-
- This enum is used as the return value for the currentCommand() function.
- This allows you to perform specific actions for particular
- commands, e.g. in a FTP client, you might want to clear the
- directory view when a list() command is started; in this case you
- can simply check in the slot connected to the start() signal if
- the currentCommand() is \c List.
-
- \value None No command is being executed.
- \value SetTransferMode set the \l{TransferMode}{transfer} mode.
- \value SetProxy switch proxying on or off.
- \value ConnectToHost connectToHost() is being executed.
- \value Login login() is being executed.
- \value Close close() is being executed.
- \value List list() is being executed.
- \value Cd cd() is being executed.
- \value Get get() is being executed.
- \value Put put() is being executed.
- \value Remove remove() is being executed.
- \value Mkdir mkdir() is being executed.
- \value Rmdir rmdir() is being executed.
- \value Rename rename() is being executed.
- \value RawCommand rawCommand() is being executed.
-
- \sa currentCommand()
-*/
-
-/*!
- \internal
- \fn void QFtp::stateChanged(int state)
-
- This signal is emitted when the state of the connection changes.
- The argument \a state is the new state of the connection; it is
- one of the \l State values.
-
- It is usually emitted in response to a connectToHost() or close()
- command, but it can also be emitted "spontaneously", e.g. when the
- server closes the connection unexpectedly.
-
- \sa connectToHost(), close(), state(), State
-*/
-
-/*!
- \internal
- \fn void QFtp::listInfo(const QUrlInfo &i);
-
- This signal is emitted for each directory entry the list() command
- finds. The details of the entry are stored in \a i.
-
- \sa list()
-*/
-
-/*!
- \internal
- \fn void QFtp::commandStarted(int id)
-
- This signal is emitted when processing the command identified by
- \a id starts.
-
- \sa commandFinished(), done()
-*/
-
-/*!
- \internal
- \fn void QFtp::commandFinished(int id, bool error)
-
- This signal is emitted when processing the command identified by
- \a id has finished. \a error is true if an error occurred during
- the processing; otherwise \a error is false.
-
- \sa commandStarted(), done(), error(), errorString()
-*/
-
-/*!
- \internal
- \fn void QFtp::done(bool error)
-
- This signal is emitted when the last pending command has finished;
- (it is emitted after the last command's commandFinished() signal).
- \a error is true if an error occurred during the processing;
- otherwise \a error is false.
-
- \sa commandFinished(), error(), errorString()
-*/
-
-/*!
- \internal
- \fn void QFtp::readyRead()
-
- This signal is emitted in response to a get() command when there
- is new data to read.
-
- If you specify a device as the second argument in the get()
- command, this signal is \e not emitted; instead the data is
- written directly to the device.
-
- You can read the data with the readAll() or read() functions.
-
- This signal is useful if you want to process the data in chunks as
- soon as it becomes available. If you are only interested in the
- complete data, just connect to the commandFinished() signal and
- read the data then instead.
-
- \sa get(), read(), readAll(), bytesAvailable()
-*/
-
-/*!
- \internal
- \fn void QFtp::dataTransferProgress(qint64 done, qint64 total)
-
- This signal is emitted in response to a get() or put() request to
- indicate the current progress of the download or upload.
-
- \a done is the amount of data that has already been transferred
- and \a total is the total amount of data to be read or written. It
- is possible that the QFtp class is not able to determine the total
- amount of data that should be transferred, in which case \a total
- is 0. (If you connect this signal to a QProgressBar, the progress
- bar shows a busy indicator if the total is 0).
-
- \warning \a done and \a total are not necessarily the size in
- bytes, since for large files these values might need to be
- "scaled" to avoid overflow.
-
- \sa get(), put(), QProgressBar
-*/
-
-/*!
- \internal
- \fn void QFtp::rawCommandReply(int replyCode, const QString &detail);
-
- This signal is emitted in response to the rawCommand() function.
- \a replyCode is the 3 digit reply code and \a detail is the text
- that follows the reply code.
-
- \sa rawCommand()
-*/
-
-/*!
- \internal
- Connects to the FTP server \a host using port \a port.
-
- The stateChanged() signal is emitted when the state of the
- connecting process changes, e.g. to \c HostLookup, then \c
- Connecting, then \c Connected.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa stateChanged(), commandStarted(), commandFinished()
-*/
-int QFtp::connectToHost(const QString &host, quint16 port)
-{
- QStringList cmds;
- cmds << host;
- cmds << QString::number((uint)port);
- int id = d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds));
- d_func()->pi.transferConnectionExtended = true;
- return id;
-}
-
-/*!
- \internal
- Logs in to the FTP server with the username \a user and the
- password \a password.
-
- The stateChanged() signal is emitted when the state of the
- connecting process changes, e.g. to \c LoggedIn.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::login(const QString &user, const QString &password)
-{
- QStringList cmds;
-
- if (user.isNull() || user.compare(QLatin1String("anonymous"), Qt::CaseInsensitive) == 0) {
- cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));
- cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));
- } else {
- cmds << (QLatin1String("USER ") + user + QLatin1String("\r\n"));
- if (!password.isNull())
- cmds << (QLatin1String("PASS ") + password + QLatin1String("\r\n"));
- }
-
- return d_func()->addCommand(new QFtpCommand(Login, cmds));
-}
-
-/*!
- \internal
- Closes the connection to the FTP server.
-
- The stateChanged() signal is emitted when the state of the
- connecting process changes, e.g. to \c Closing, then \c
- Unconnected.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa stateChanged(), commandStarted(), commandFinished()
-*/
-int QFtp::close()
-{
- return d_func()->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n"))));
-}
-
-/*!
- \internal
- Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.
-
- \sa QFtp::TransferMode
-*/
-int QFtp::setTransferMode(TransferMode mode)
-{
- int id = d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
- d_func()->pi.transferConnectionExtended = true;
- d_func()->transferMode = mode;
- return id;
-}
-
-/*!
- \internal
- Enables use of the FTP proxy on host \a host and port \a
- port. Calling this function with \a host empty disables proxying.
-
- QFtp does not support FTP-over-HTTP proxy servers. Use
- QNetworkAccessManager for this.
-*/
-int QFtp::setProxy(const QString &host, quint16 port)
-{
- QStringList args;
- args << host << QString::number(port);
- return d_func()->addCommand(new QFtpCommand(SetProxy, args));
-}
-
-/*!
- \internal
- Lists the contents of directory \a dir on the FTP server. If \a
- dir is empty, it lists the contents of the current directory.
-
- The listInfo() signal is emitted for each directory entry found.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa listInfo(), commandStarted(), commandFinished()
-*/
-int QFtp::list(const QString &dir)
-{
- QStringList cmds;
- cmds << QLatin1String("TYPE A\r\n");
- cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
- if (dir.isEmpty())
- cmds << QLatin1String("LIST\r\n");
- else
- cmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));
- return d_func()->addCommand(new QFtpCommand(List, cmds));
-}
-
-/*!
- \internal
- Changes the working directory of the server to \a dir.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::cd(const QString &dir)
-{
- return d_func()->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n"))));
-}
-
-/*!
- \internal
- Downloads the file \a file from the server.
-
- If \a dev is 0, then the readyRead() signal is emitted when there
- is data available to read. You can then read the data with the
- read() or readAll() functions.
-
- If \a dev is not \nullptr, the data is written directly to the device
- \a dev. Make sure that the \a dev pointer is valid for the duration
- of the operation (it is safe to delete it when the
- commandFinished() signal is emitted). In this case the readyRead()
- signal is \e not emitted and you cannot read data with the
- read() or readAll() functions.
-
- If you don't read the data immediately it becomes available, i.e.
- when the readyRead() signal is emitted, it is still available
- until the next command is started.
-
- For example, if you want to present the data to the user as soon
- as there is something available, connect to the readyRead() signal
- and read the data immediately. On the other hand, if you only want
- to work with the complete data, you can connect to the
- commandFinished() signal and read the data when the get() command
- is finished.
-
- The data is transferred as Binary or Ascii depending on the value
- of \a type.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa readyRead(), dataTransferProgress(), commandStarted(),
- commandFinished()
-*/
-int QFtp::get(const QString &file, QIODevice *dev, TransferType type)
-{
- QStringList cmds;
- if (type == Binary)
- cmds << QLatin1String("TYPE I\r\n");
- else
- cmds << QLatin1String("TYPE A\r\n");
- cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");
- cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
- cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");
- return d_func()->addCommand(new QFtpCommand(Get, cmds, dev));
-}
-
-/*!
- \internal
- \overload
-
- Writes a copy of the given \a data to the file called \a file on
- the server. The progress of the upload is reported by the
- dataTransferProgress() signal.
-
- The data is transferred as Binary or Ascii depending on the value
- of \a type.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- Since this function takes a copy of the \a data, you can discard
- your own copy when this function returns.
-
- \sa dataTransferProgress(), commandStarted(), commandFinished()
-*/
-int QFtp::put(const QByteArray &data, const QString &file, TransferType type)
-{
- QStringList cmds;
- if (type == Binary)
- cmds << QLatin1String("TYPE I\r\n");
- else
- cmds << QLatin1String("TYPE A\r\n");
- cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
- cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");
- cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
- return d_func()->addCommand(new QFtpCommand(Put, cmds, data));
-}
-
-/*!
- \internal
- Reads the data from the IO device \a dev, and writes it to the
- file called \a file on the server. The data is read in chunks from
- the IO device, so this overload allows you to transmit large
- amounts of data without the need to read all the data into memory
- at once.
-
- The data is transferred as Binary or Ascii depending on the value
- of \a type.
-
- Make sure that the \a dev pointer is valid for the duration of the
- operation (it is safe to delete it when the commandFinished() is
- emitted).
-*/
-int QFtp::put(QIODevice *dev, const QString &file, TransferType type)
-{
- QStringList cmds;
- if (type == Binary)
- cmds << QLatin1String("TYPE I\r\n");
- else
- cmds << QLatin1String("TYPE A\r\n");
- cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
- if (!dev->isSequential())
- cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");
- cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
- return d_func()->addCommand(new QFtpCommand(Put, cmds, dev));
-}
-
-/*!
- \internal
- Deletes the file called \a file from the server.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::remove(const QString &file)
-{
- return d_func()->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n"))));
-}
-
-/*!
- \internal
- Creates a directory called \a dir on the server.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::mkdir(const QString &dir)
-{
- return d_func()->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n"))));
-}
-
-/*!
- \internal
- Removes the directory called \a dir from the server.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::rmdir(const QString &dir)
-{
- return d_func()->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n"))));
-}
-
-/*!
- \internal
- Renames the file called \a oldname to \a newname on the server.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa commandStarted(), commandFinished()
-*/
-int QFtp::rename(const QString &oldname, const QString &newname)
-{
- QStringList cmds;
- cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");
- cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");
- return d_func()->addCommand(new QFtpCommand(Rename, cmds));
-}
-
-/*!
- \internal
- Sends the raw FTP command \a command to the FTP server. This is
- useful for low-level FTP access. If the operation you wish to
- perform has an equivalent QFtp function, we recommend using the
- function instead of raw FTP commands since the functions are
- easier and safer.
-
- The function does not block and returns immediately. The command
- is scheduled, and its execution is performed asynchronously. The
- function returns a unique identifier which is passed by
- commandStarted() and commandFinished().
-
- When the command is started the commandStarted() signal is
- emitted. When it is finished the commandFinished() signal is
- emitted.
-
- \sa rawCommandReply(), commandStarted(), commandFinished()
-*/
-int QFtp::rawCommand(const QString &command)
-{
- const QString cmd = QStringRef(&command).trimmed() + QLatin1String("\r\n");
- return d_func()->addCommand(new QFtpCommand(RawCommand, QStringList(cmd)));
-}
-
-/*!
- \internal
- Returns the number of bytes that can be read from the data socket
- at the moment.
-
- \sa get(), readyRead(), read(), readAll()
-*/
-qint64 QFtp::bytesAvailable() const
-{
- return d_func()->pi.dtp.bytesAvailable();
-}
-
-/*!
- \internal
- Reads \a maxlen bytes from the data socket into \a data and
- returns the number of bytes read. Returns -1 if an error occurred.
-
- \sa get(), readyRead(), bytesAvailable(), readAll()
-*/
-qint64 QFtp::read(char *data, qint64 maxlen)
-{
- return d_func()->pi.dtp.read(data, maxlen);
-}
-
-/*!
- \internal
- Reads all the bytes available from the data socket and returns
- them.
-
- \sa get(), readyRead(), bytesAvailable(), read()
-*/
-QByteArray QFtp::readAll()
-{
- return d_func()->pi.dtp.readAll();
-}
-
-/*!
- \internal
- Aborts the current command and deletes all scheduled commands.
-
- If there is an unfinished command (i.e. a command for which the
- commandStarted() signal has been emitted, but for which the
- commandFinished() signal has not been emitted), this function
- sends an \c ABORT command to the server. When the server replies
- that the command is aborted, the commandFinished() signal with the
- \c error argument set to \c true is emitted for the command. Due
- to timing issues, it is possible that the command had already
- finished before the abort request reached the server, in which
- case, the commandFinished() signal is emitted with the \c error
- argument set to \c false.
-
- For all other commands that are affected by the abort(), no
- signals are emitted.
-
- If you don't start further FTP commands directly after the
- abort(), there won't be any scheduled commands and the done()
- signal is emitted.
-
- \warning Some FTP servers, for example the BSD FTP daemon (version
- 0.3), wrongly return a positive reply even when an abort has
- occurred. For these servers the commandFinished() signal has its
- error flag set to \c false, even though the command did not
- complete successfully.
-
- \sa clearPendingCommands()
-*/
-void QFtp::abort()
-{
- if (d_func()->pending.isEmpty())
- return;
-
- clearPendingCommands();
- d_func()->pi.abort();
-}
-
-/*!
- \internal
- Clears the last error.
-
- \sa currentCommand()
-*/
-void QFtp::clearError()
-{
- d_func()->error = NoError;
-}
-
-/*!
- \internal
- Returns the identifier of the FTP command that is being executed
- or 0 if there is no command being executed.
-
- \sa currentCommand()
-*/
-int QFtp::currentId() const
-{
- if (d_func()->pending.isEmpty())
- return 0;
- return d_func()->pending.first()->id;
-}
-
-/*!
- \internal
- Returns the command type of the FTP command being executed or \c
- None if there is no command being executed.
-
- \sa currentId()
-*/
-QFtp::Command QFtp::currentCommand() const
-{
- if (d_func()->pending.isEmpty())
- return None;
- return d_func()->pending.first()->command;
-}
-
-/*!
- \internal
- Returns the QIODevice pointer that is used by the FTP command to read data
- from or store data to. If there is no current FTP command being executed or
- if the command does not use an IO device, this function returns \nullptr.
-
- This function can be used to delete the QIODevice in the slot connected to
- the commandFinished() signal.
-
- \sa get(), put()
-*/
-QIODevice* QFtp::currentDevice() const
-{
- if (d_func()->pending.isEmpty())
- return nullptr;
- QFtpCommand *c = d_func()->pending.first();
- if (c->is_ba)
- return nullptr;
- return c->data.dev;
-}
-
-/*!
- \internal
- Returns \c true if there are any commands scheduled that have not yet
- been executed; otherwise returns \c false.
-
- The command that is being executed is \e not considered as a
- scheduled command.
-
- \sa clearPendingCommands(), currentId(), currentCommand()
-*/
-bool QFtp::hasPendingCommands() const
-{
- return d_func()->pending.count() > 1;
-}
-
-/*!
- \internal
- Deletes all pending commands from the list of scheduled commands.
- This does not affect the command that is being executed. If you
- want to stop this as well, use abort().
-
- \sa hasPendingCommands(), abort()
-*/
-void QFtp::clearPendingCommands()
-{
- // delete all entires except the first one
- while (d_func()->pending.count() > 1)
- delete d_func()->pending.takeLast();
-}
-
-/*!
- \internal
- Returns the current state of the object. When the state changes,
- the stateChanged() signal is emitted.
-
- \sa State, stateChanged()
-*/
-QFtp::State QFtp::state() const
-{
- return d_func()->state;
-}
-
-/*!
- \internal
- Returns the last error that occurred. This is useful to find out
- what went wrong when receiving a commandFinished() or a done()
- signal with the \c error argument set to \c true.
-
- If you start a new command, the error status is reset to \c NoError.
-*/
-QFtp::Error QFtp::error() const
-{
- return d_func()->error;
-}
-
-/*!
- \internal
- Returns a human-readable description of the last error that
- occurred. This is useful for presenting a error message to the
- user when receiving a commandFinished() or a done() signal with
- the \c error argument set to \c true.
-
- The error string is often (but not always) the reply from the
- server, so it is not always possible to translate the string. If
- the message comes from Qt, the string has already passed through
- tr().
-*/
-QString QFtp::errorString() const
-{
- return d_func()->errorString;
-}
-
-/*! \internal
-*/
-void QFtpPrivate::_q_startNextCommand()
-{
- Q_Q(QFtp);
- if (pending.isEmpty())
- return;
- QFtpCommand *c = pending.constFirst();
-
- error = QFtp::NoError;
- errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));
-
- if (q->bytesAvailable())
- q->readAll(); // clear the data
- emit q->commandStarted(c->id);
-
- // Proxy support, replace the Login argument in place, then fall
- // through.
- if (c->command == QFtp::Login && !proxyHost.isEmpty()) {
- QString loginString;
- loginString += QStringRef(&c->rawCmds.constFirst()).trimmed() + QLatin1Char('@') + host;
- if (port && port != 21)
- loginString += QLatin1Char(':') + QString::number(port);
- loginString += QLatin1String("\r\n");
- c->rawCmds[0] = loginString;
- }
-
- if (c->command == QFtp::SetTransferMode) {
- _q_piFinished(QLatin1String("Transfer mode set"));
- } else if (c->command == QFtp::SetProxy) {
- proxyHost = c->rawCmds.at(0);
- proxyPort = c->rawCmds.at(1).toUInt();
- c->rawCmds.clear();
- _q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));
- } else if (c->command == QFtp::ConnectToHost) {
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the PI
- pi.setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
- if (!proxyHost.isEmpty()) {
- host = c->rawCmds.at(0);
- port = c->rawCmds.at(1).toUInt();
- pi.connectToHost(proxyHost, proxyPort);
- } else {
- pi.connectToHost(c->rawCmds.at(0), c->rawCmds.at(1).toUInt());
- }
- } else {
- if (c->command == QFtp::Put) {
- if (c->is_ba) {
- pi.dtp.setData(c->data.ba);
- pi.dtp.setBytesTotal(c->data.ba->size());
- } else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {
- pi.dtp.setDevice(c->data.dev);
- if (c->data.dev->isSequential()) {
- pi.dtp.setBytesTotal(0);
- pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));
- pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));
- } else {
- pi.dtp.setBytesTotal(c->data.dev->size());
- }
- }
- } else if (c->command == QFtp::Get) {
- if (!c->is_ba && c->data.dev) {
- pi.dtp.setDevice(c->data.dev);
- }
- } else if (c->command == QFtp::Close) {
- state = QFtp::Closing;
- emit q->stateChanged(state);
- }
- pi.sendCommands(c->rawCmds);
- }
-}
-
-/*! \internal
-*/
-void QFtpPrivate::_q_piFinished(const QString&)
-{
- if (pending.isEmpty())
- return;
- QFtpCommand *c = pending.constFirst();
-
- if (c->command == QFtp::Close) {
- // The order of in which the slots are called is arbitrary, so
- // disconnect the SIGNAL-SIGNAL temporary to make sure that we
- // don't get the commandFinished() signal before the stateChanged()
- // signal.
- if (state != QFtp::Unconnected) {
- close_waitForStateChange = true;
- return;
- }
- }
- emit q_func()->commandFinished(c->id, false);
- pending.removeFirst();
-
- delete c;
-
- if (pending.isEmpty()) {
- emit q_func()->done(false);
- } else {
- _q_startNextCommand();
- }
-}
-
-/*! \internal
-*/
-void QFtpPrivate::_q_piError(int errorCode, const QString &text)
-{
- Q_Q(QFtp);
-
- if (pending.isEmpty()) {
- qWarning("QFtpPrivate::_q_piError was called without pending command!");
- return;
- }
-
- QFtpCommand *c = pending.constFirst();
-
- // non-fatal errors
- if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
- pi.dtp.setBytesTotal(0);
- return;
- } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
- return;
- }
-
- error = QFtp::Error(errorCode);
- switch (q->currentCommand()) {
- case QFtp::ConnectToHost:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Login:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
- .arg(text);
- break;
- case QFtp::List:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Cd:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Get:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Put:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Remove:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Mkdir:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
- .arg(text);
- break;
- case QFtp::Rmdir:
- errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
- .arg(text);
- break;
- default:
- errorString = text;
- break;
- }
-
- pi.clearPendingCommands();
- q->clearPendingCommands();
- emit q->commandFinished(c->id, true);
-
- pending.removeFirst();
- delete c;
- if (pending.isEmpty())
- emit q->done(true);
- else
- _q_startNextCommand();
-}
-
-/*! \internal
-*/
-void QFtpPrivate::_q_piConnectState(int connectState)
-{
- state = QFtp::State(connectState);
- emit q_func()->stateChanged(state);
- if (close_waitForStateChange) {
- close_waitForStateChange = false;
- _q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));
- }
-}
-
-/*! \internal
-*/
-void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
-{
- if (q_func()->currentCommand() == QFtp::RawCommand) {
- pi.rawCommand = true;
- emit q_func()->rawCommandReply(code, text);
- }
-}
-
-/*!
- \internal
- Destructor.
-*/
-QFtp::~QFtp()
-{
- abort();
- close();
-}
-
-QT_END_NAMESPACE
-
-#include "qftp.moc"
-
-#include "moc_qftp_p.cpp"
diff --git a/src/network/access/qftp_p.h b/src/network/access/qftp_p.h
deleted file mode 100644
index a55429933b..0000000000
--- a/src/network/access/qftp_p.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the Network Access API. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#ifndef QFTP_P_H
-#define QFTP_P_H
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qstring.h>
-#include <private/qurlinfo_p.h>
-#include <QtCore/qobject.h>
-
-QT_REQUIRE_CONFIG(ftp);
-
-QT_BEGIN_NAMESPACE
-
-class QFtpPrivate;
-
-class Q_AUTOTEST_EXPORT QFtp : public QObject
-{
- Q_OBJECT
-
-public:
- explicit QFtp(QObject *parent = nullptr);
- virtual ~QFtp();
-
- enum State {
- Unconnected,
- HostLookup,
- Connecting,
- Connected,
- LoggedIn,
- Closing
- };
- enum Error {
- NoError,
- UnknownError,
- HostNotFound,
- ConnectionRefused,
- NotConnected
- };
- enum Command {
- None,
- SetTransferMode,
- SetProxy,
- ConnectToHost,
- Login,
- Close,
- List,
- Cd,
- Get,
- Put,
- Remove,
- Mkdir,
- Rmdir,
- Rename,
- RawCommand
- };
- enum TransferMode {
- Active,
- Passive
- };
- enum TransferType {
- Binary,
- Ascii
- };
-
- int setProxy(const QString &host, quint16 port);
- int connectToHost(const QString &host, quint16 port=21);
- int login(const QString &user = QString(), const QString &password = QString());
- int close();
- int setTransferMode(TransferMode mode);
- int list(const QString &dir = QString());
- int cd(const QString &dir);
- int get(const QString &file, QIODevice *dev=nullptr, TransferType type = Binary);
- int put(const QByteArray &data, const QString &file, TransferType type = Binary);
- int put(QIODevice *dev, const QString &file, TransferType type = Binary);
- int remove(const QString &file);
- int mkdir(const QString &dir);
- int rmdir(const QString &dir);
- int rename(const QString &oldname, const QString &newname);
-
- int rawCommand(const QString &command);
-
- qint64 bytesAvailable() const;
- qint64 read(char *data, qint64 maxlen);
- QByteArray readAll();
-
- int currentId() const;
- QIODevice* currentDevice() const;
- Command currentCommand() const;
- bool hasPendingCommands() const;
- void clearPendingCommands();
-
- State state() const;
-
- Error error() const;
- QString errorString() const;
-
-public Q_SLOTS:
- void abort();
-
-Q_SIGNALS:
- void stateChanged(int);
- void listInfo(const QUrlInfo&);
- void readyRead();
- void dataTransferProgress(qint64, qint64);
- void rawCommandReply(int, const QString&);
-
- void commandStarted(int);
- void commandFinished(int, bool);
- void done(bool);
-
-protected:
- void clearError();
-
-private:
- Q_DISABLE_COPY_MOVE(QFtp)
- Q_DECLARE_PRIVATE(QFtp)
-
- Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand())
- Q_PRIVATE_SLOT(d_func(), void _q_piFinished(const QString&))
- Q_PRIVATE_SLOT(d_func(), void _q_piError(int, const QString&))
- Q_PRIVATE_SLOT(d_func(), void _q_piConnectState(int))
- Q_PRIVATE_SLOT(d_func(), void _q_piFtpReply(int, const QString&))
-};
-
-QT_END_NAMESPACE
-
-#endif // QFTP_P_H
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 0cef0ad3dc..21ed08ce4a 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhsts_p.h"
+#include "qhttpheaders.h"
+
#include "QtCore/private/qipaddress_p.h"
-#include "QtCore/qvector.h"
#include "QtCore/qlist.h"
#if QT_CONFIG(settings)
@@ -77,7 +42,7 @@ static bool is_valid_domain_name(const QString &host)
return true;
}
-void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+void QHstsCache::updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url)
{
if (!url.isValid())
@@ -93,7 +58,7 @@ void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &h
}
}
-void QHstsCache::updateFromPolicies(const QVector<QHstsPolicy> &policies)
+void QHstsCache::updateFromPolicies(const QList<QHstsPolicy> &policies)
{
for (const auto &policy : policies)
updateKnownHost(policy.host(), policy.expiry(), policy.includesSubDomains());
@@ -194,7 +159,7 @@ bool QHstsCache::isKnownHost(const QUrl &url) const
bool superDomainMatch = false;
const QString hostNameAsString(url.host());
- HostName nameToTest(static_cast<QStringRef>(&hostNameAsString));
+ HostName nameToTest(QStringView{hostNameAsString});
while (nameToTest.fragment.size()) {
auto const pos = knownHosts.find(nameToTest);
if (pos != knownHosts.end()) {
@@ -211,7 +176,7 @@ bool QHstsCache::isKnownHost(const QUrl &url) const
}
}
- const int dot = nameToTest.fragment.indexOf(QLatin1Char('.'));
+ const qsizetype dot = nameToTest.fragment.indexOf(u'.');
if (dot == -1)
break;
@@ -227,9 +192,9 @@ void QHstsCache::clear()
knownHosts.clear();
}
-QVector<QHstsPolicy> QHstsCache::policies() const
+QList<QHstsPolicy> QHstsCache::policies() const
{
- QVector<QHstsPolicy> values;
+ QList<QHstsPolicy> values;
values.reserve(int(knownHosts.size()));
for (const auto &host : knownHosts)
values << host.second;
@@ -250,7 +215,7 @@ void QHstsCache::setStore(QHstsStore *store)
// (and thus the cached policy takes priority over whatever policy we
// had in the store for the same host, if any).
if (knownHosts.size()) {
- const QVector<QHstsPolicy> observed(policies());
+ const QList<QHstsPolicy> observed(policies());
for (const auto &policy : observed)
hstsStore->addToObserved(policy);
hstsStore->synchronize();
@@ -260,7 +225,7 @@ void QHstsCache::setStore(QHstsStore *store)
// the store knows about (well, it can happen we synchronize again as a
// result if some policies managed to expire or if we add a new one
// from the store to cache):
- const QVector<QHstsPolicy> restored(store->readPolicies());
+ const QList<QHstsPolicy> restored(store->readPolicies());
updateFromPolicies(restored);
}
}
@@ -323,7 +288,7 @@ static bool isSeparator(char c)
return isLWS(c) || std::find(separators, end, c) != end;
}
-static QByteArray unescapeMaxAge(const QByteArray &value)
+static QByteArrayView unescapeMaxAge(QByteArrayView value)
{
if (value.size() < 2 || value[0] != '"')
return value;
@@ -361,27 +326,25 @@ quoted-pair = "\" CHAR
*/
-bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
+bool QHstsHeaderParser::parse(const QHttpHeaders &headers)
{
- for (const auto &h : headers) {
- // We use '==' since header name was already 'trimmed' for us:
- if (h.first == "Strict-Transport-Security") {
- header = h.second;
- // RFC6797, 8.1:
- //
- // The UA MUST ignore any STS header fields not conforming to the
- // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
- // Response Header Field").
- //
- // If a UA receives more than one STS header field in an HTTP
- // response message over secure transport, then the UA MUST process
- // only the first such header field.
- //
- // We read this as: ignore all invalid headers and take the first valid:
- if (parseSTSHeader() && maxAgeFound) {
- expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
- return true;
- }
+ for (const auto &value : headers.values(
+ QHttpHeaders::WellKnownHeader::StrictTransportSecurity)) {
+ header = value;
+ // RFC6797, 8.1:
+ //
+ // The UA MUST ignore any STS header fields not conforming to the
+ // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
+ // Response Header Field").
+ //
+ // If a UA receives more than one STS header field in an HTTP
+ // response message over secure transport, then the UA MUST process
+ // only the first such header field.
+ //
+ // We read this as: ignore all invalid headers and take the first valid:
+ if (parseSTSHeader() && maxAgeFound) {
+ expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
+ return true;
}
}
@@ -437,7 +400,7 @@ bool QHstsHeaderParser::parseDirective()
if (token == ";") // That's a weird grammar, but that's what it is.
return true;
- if (!isTOKEN(token[0])) // Not a valid directive-name.
+ if (!isTOKEN(token.at(0))) // Not a valid directive-name.
return false;
const QByteArray directiveName = token;
@@ -482,7 +445,7 @@ bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArra
return false;
}
- const QByteArray unescapedValue = unescapeMaxAge(value);
+ const QByteArrayView unescapedValue = unescapeMaxAge(value);
if (!unescapedValue.size())
return false;
@@ -518,13 +481,13 @@ bool QHstsHeaderParser::nextToken()
// Fortunately enough, by this point qhttpnetworkreply already got rid of
// [CRLF] parts, but we can have 1*(SP|HT) yet.
- while (tokenPos < header.size() && isLWS(header[tokenPos]))
+ while (tokenPos < header.size() && isLWS(header.at(tokenPos)))
++tokenPos;
if (tokenPos == header.size())
return true;
- const char ch = header[tokenPos];
+ const char ch = header.at(tokenPos);
if (ch == ';' || ch == '=') {
token.append(ch);
++tokenPos;
@@ -538,17 +501,17 @@ bool QHstsHeaderParser::nextToken()
if (ch == '"') {
int last = tokenPos + 1;
while (last < header.size()) {
- if (header[last] == '"') {
+ if (header.at(last) == '"') {
// The end of a quoted-string.
break;
- } else if (header[last] == '\\') {
+ } else if (header.at(last) == '\\') {
// quoted-pair = "\" CHAR
- if (last + 1 < header.size() && isCHAR(header[last + 1]))
+ if (last + 1 < header.size() && isCHAR(header.at(last + 1)))
last += 2;
else
return false;
} else {
- if (!isTEXT(header[last]))
+ if (!isTEXT(header.at(last)))
return false;
++last;
}
@@ -569,7 +532,7 @@ bool QHstsHeaderParser::nextToken()
return false;
int last = tokenPos + 1;
- while (last < header.size() && isTOKEN(header[last]))
+ while (last < header.size() && isTOKEN(header.at(last)))
++last;
token = header.mid(tokenPos, last - tokenPos);
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
index c219d9eab5..ff9378197b 100644
--- a/src/network/access/qhsts_p.h
+++ b/src/network/access/qhsts_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTS_P_H
#define QHSTS_P_H
@@ -61,27 +25,27 @@
#include <QtCore/qglobal.h>
#include <QtCore/qpair.h>
#include <QtCore/qurl.h>
+#include <QtCore/qcontainerfwd.h>
#include <map>
QT_BEGIN_NAMESPACE
-template<typename T> class QList;
-template <typename T> class QVector;
+class QHttpHeaders;
class Q_AUTOTEST_EXPORT QHstsCache
{
public:
- void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+ void updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url);
- void updateFromPolicies(const QVector<QHstsPolicy> &hosts);
+ void updateFromPolicies(const QList<QHstsPolicy> &hosts);
void updateKnownHost(const QUrl &url, const QDateTime &expires,
bool includeSubDomains);
bool isKnownHost(const QUrl &url) const;
void clear();
- QVector<QHstsPolicy> policies() const;
+ QList<QHstsPolicy> policies() const;
#if QT_CONFIG(settings)
void setStore(class QHstsStore *store);
@@ -95,18 +59,18 @@ private:
struct HostName
{
explicit HostName(const QString &n) : name(n) { }
- explicit HostName(const QStringRef &r) : fragment(r) { }
+ explicit HostName(QStringView r) : fragment(r) { }
bool operator < (const HostName &rhs) const
{
if (fragment.size()) {
if (rhs.fragment.size())
return fragment < rhs.fragment;
- return fragment < QStringRef(&rhs.name);
+ return fragment < QStringView{rhs.name};
}
if (rhs.fragment.size())
- return QStringRef(&name) < rhs.fragment;
+ return QStringView{name} < rhs.fragment;
return name < rhs.name;
}
@@ -115,7 +79,7 @@ private:
// name, removing subdomain names (such HostName object is 'transient', it
// must not outlive the original QString object.
QString name;
- QStringRef fragment;
+ QStringView fragment;
};
mutable std::map<HostName, QHstsPolicy> knownHosts;
@@ -128,7 +92,7 @@ class Q_AUTOTEST_EXPORT QHstsHeaderParser
{
public:
- bool parse(const QList<QPair<QByteArray, QByteArray>> &headers);
+ bool parse(const QHttpHeaders &headers);
QDateTime expirationDate() const { return expiry; }
bool includeSubDomains() const { return subDomainsFound; }
diff --git a/src/network/access/qhstspolicy.cpp b/src/network/access/qhstspolicy.cpp
index 46c9c22510..323e562c3c 100644
--- a/src/network/access/qhstspolicy.cpp
+++ b/src/network/access/qhstspolicy.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhstspolicy.h"
@@ -57,8 +21,8 @@ QT_BEGIN_NAMESPACE
RFC6797.
You can set expiry time and host name for this policy, and control whether it
- applies to subdomains, either in the constructor or by calling setExpiry(),
- setHost() and setIncludesSubdomains().
+ applies to subdomains, either in the constructor or by calling \l setExpiry(),
+ \l setHost() and \l setIncludesSubDomains().
\sa QNetworkAccessManager::setStrictTransportSecurityEnabled()
*/
@@ -86,12 +50,25 @@ public:
};
/*!
- Returns \c true if the two policies have the same host and expiration date
- while agreeing on whether to include or exclude subdomains.
+ \fn bool QHstsPolicy::operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
+
+ Returns \c true if the two policies \a lhs and \a rhs have the same host and
+ expiration date while agreeing on whether to include or exclude subdomains.
+*/
+
+/*!
+ \fn bool QHstsPolicy::operator!=(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
+
+ Returns \c true if the two policies \a lhs and \a rhs do not have the same host
+ or expiration date, or do not agree on whether to include or exclude subdomains.
+*/
+
+/*!
+ \internal
*/
-bool operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
+bool QHstsPolicy::isEqual(const QHstsPolicy &other) const
{
- return *lhs.d == *rhs.d;
+ return *d == *other.d;
}
/*!
diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h
index f1b2ee99e5..af5841906c 100644
--- a/src/network/access/qhstspolicy.h
+++ b/src/network/access/qhstspolicy.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTSPOLICY_H
#define QHSTSPOLICY_H
@@ -68,7 +32,7 @@ public:
QHstsPolicy &operator=(QHstsPolicy &&other) noexcept { swap(other); return *this; }
~QHstsPolicy();
- void swap(QHstsPolicy &other) noexcept { qSwap(d, other.d); }
+ void swap(QHstsPolicy &other) noexcept { d.swap(other.d); }
void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode);
QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const;
@@ -80,21 +44,18 @@ public:
bool isExpired() const;
private:
-
QSharedDataPointer<QHstsPolicyPrivate> d;
- friend Q_NETWORK_EXPORT bool operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs);
+ bool isEqual(const QHstsPolicy &other) const;
+ friend bool operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
+ { return !lhs.isEqual(rhs); }
};
Q_DECLARE_SHARED(QHstsPolicy)
Q_DECLARE_OPERATORS_FOR_FLAGS(QHstsPolicy::PolicyFlags)
-Q_NETWORK_EXPORT bool operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs);
-
-inline bool operator!=(const QHstsPolicy &lhs, const QHstsPolicy &rhs)
-{
- return !(lhs == rhs);
-}
QT_END_NAMESPACE
diff --git a/src/network/access/qhstsstore.cpp b/src/network/access/qhstsstore.cpp
index 6d7f60ba8d..a972a90ee7 100644
--- a/src/network/access/qhstsstore.cpp
+++ b/src/network/access/qhstsstore.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhstsstore_p.h"
#include "qhstspolicy.h"
@@ -52,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static QString host_name_to_settings_key(const QString &hostName)
{
const QByteArray hostNameAsHex(hostName.toUtf8().toHex());
@@ -76,13 +42,13 @@ QHstsStore::~QHstsStore()
synchronize();
}
-QVector<QHstsPolicy> QHstsStore::readPolicies()
+QList<QHstsPolicy> QHstsStore::readPolicies()
{
// This function only attempts to read policies, making no decision about
// expired policies. It's up to a user (QHstsCache) to mark these policies
// for deletion and sync the store later. But we immediately remove keys/values
// (if the store isWritable) for the policies that we fail to read.
- QVector<QHstsPolicy> policies;
+ QList<QHstsPolicy> policies;
beginHstsGroups();
@@ -114,7 +80,7 @@ void QHstsStore::synchronize()
if (observedPolicies.size()) {
beginHstsGroups();
- for (const QHstsPolicy &policy : qAsConst(observedPolicies)) {
+ for (const QHstsPolicy &policy : std::as_const(observedPolicies)) {
const QString key(host_name_to_settings_key(policy.host()));
// If we fail to write a new, updated policy, we also remove the old one.
if (policy.isExpired() || !serializePolicy(key, policy))
@@ -136,13 +102,13 @@ QString QHstsStore::absoluteFilePath(const QString &dirName)
{
const QDir dir(dirName.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
: dirName);
- return dir.absoluteFilePath(QLatin1String("hstsstore"));
+ return dir.absoluteFilePath("hstsstore"_L1);
}
void QHstsStore::beginHstsGroups()
{
- store.beginGroup(QLatin1String("StrictTransportSecurity"));
- store.beginGroup(QLatin1String("Policies"));
+ store.beginGroup("StrictTransportSecurity"_L1);
+ store.beginGroup("Policies"_L1);
}
void QHstsStore::endHstsGroups()
diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h
index 5338d15592..c1ec3af122 100644
--- a/src/network/access/qhstsstore_p.h
+++ b/src/network/access/qhstsstore_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTSSTORE_P_H
#define QHSTSSTORE_P_H
@@ -55,8 +19,8 @@
QT_REQUIRE_CONFIG(settings);
+#include <QtCore/qlist.h>
#include <QtCore/qsettings.h>
-#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
@@ -70,7 +34,7 @@ public:
explicit QHstsStore(const QString &dirName);
~QHstsStore();
- QVector<QHstsPolicy> readPolicies();
+ QList<QHstsPolicy> readPolicies();
void addToObserved(const QHstsPolicy &policy);
void synchronize();
@@ -84,7 +48,7 @@ private:
void evictPolicy(const QString &key);
void endHstsGroups();
- QVector<QHstsPolicy> observedPolicies;
+ QList<QHstsPolicy> observedPolicies;
QSettings store;
Q_DISABLE_COPY_MOVE(QHstsStore)
diff --git a/src/network/access/qhttp1configuration.cpp b/src/network/access/qhttp1configuration.cpp
new file mode 100644
index 0000000000..cfa929bca5
--- /dev/null
+++ b/src/network/access/qhttp1configuration.cpp
@@ -0,0 +1,154 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp1configuration.h"
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/qhashfunctions.h>
+
+QT_BEGIN_NAMESPACE
+
+// QHttp1ConfigurationPrivate is unused until we need it:
+static_assert(sizeof(QHttp1Configuration) == sizeof(void*),
+ "You have added too many members to QHttp1Configuration::ShortData. "
+ "Decrease their size or switch to using a d-pointer.");
+
+/*!
+ \class QHttp1Configuration
+ \brief The QHttp1Configuration class controls HTTP/1 parameters and settings.
+ \since 6.5
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ QHttp1Configuration controls HTTP/1 parameters and settings that
+ QNetworkAccessManager will use to send requests and process responses.
+
+ \note The configuration must be set before the first request
+ was sent to a given host (and thus an HTTP/1 session established).
+
+ \sa QNetworkRequest::setHttp1Configuration(), QNetworkRequest::http1Configuration(), QNetworkAccessManager
+*/
+
+/*!
+ Default constructs a QHttp1Configuration object.
+*/
+QHttp1Configuration::QHttp1Configuration()
+ : u(ShortData{6, {}}) // QHttpNetworkConnectionPrivate::defaultHttpChannelCount
+{
+}
+
+/*!
+ Copy-constructs this QHttp1Configuration.
+*/
+QHttp1Configuration::QHttp1Configuration(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration::QHttp1Configuration(QHttp1Configuration &&other)
+
+ Move-constructs this QHttp1Configuration from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Copy-assigns \a other to this QHttp1Configuration.
+*/
+QHttp1Configuration &QHttp1Configuration::operator=(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration &QHttp1Configuration::operator=(QHttp1Configuration &&)
+
+ Move-assigns \a other to this QHttp1Configuration.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Destructor.
+*/
+QHttp1Configuration::~QHttp1Configuration()
+ = default;
+
+/*!
+ Sets the number of connections (minimum: 1; maximum: 255)
+ used per http(s) \e{host}:\e{port} combination to \a number.
+
+ If \a number is ≤ 0, does nothing. If \a number is > 255, 255 is used.
+
+ \sa numberOfConnectionsPerHost
+*/
+void QHttp1Configuration::setNumberOfConnectionsPerHost(qsizetype number)
+{
+ auto n = qt_saturate<std::uint8_t>(number);
+ if (n == 0)
+ return;
+ u.data.numConnectionsPerHost = n;
+}
+
+/*!
+ Returns the number of connections used per http(s) \c{host}:\e{port}
+ combination. The default is six (6).
+
+ \sa setNumberOfConnectionsPerHost
+*/
+qsizetype QHttp1Configuration::numberOfConnectionsPerHost() const
+{
+ return u.data.numConnectionsPerHost;
+}
+
+/*!
+ \fn void QHttp1Configuration::swap(QHttp1Configuration &other)
+
+ Swaps this HTTP/1 configuration with \a other. This operation is very fast
+ and never fails.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs represent the same set of HTTP/1
+ parameters.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs do not represent the same set of
+ HTTP/1 parameters.
+*/
+
+/*!
+ \fn size_t QHttp1Configuration::qHash(const QHttp1Configuration &key, size_t seed)
+ \since 6.5
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+
+/*!
+ \internal
+*/
+bool QHttp1Configuration::equals(const QHttp1Configuration &other) const noexcept
+{
+ return u.data.numConnectionsPerHost == other.u.data.numConnectionsPerHost;
+}
+
+/*!
+ \internal
+*/
+size_t QHttp1Configuration::hash(size_t seed) const noexcept
+{
+ return qHash(u.data.numConnectionsPerHost, seed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttp1configuration.h b/src/network/access/qhttp1configuration.h
new file mode 100644
index 0000000000..128b8aa5aa
--- /dev/null
+++ b/src/network/access/qhttp1configuration.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTP1CONFIGURATION_H
+#define QHTTP1CONFIGURATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <utility>
+#include <cstdint>
+
+QT_REQUIRE_CONFIG(http);
+
+QT_BEGIN_NAMESPACE
+
+class QHttp1ConfigurationPrivate;
+class QHttp1Configuration
+{
+public:
+ Q_NETWORK_EXPORT QHttp1Configuration();
+ Q_NETWORK_EXPORT QHttp1Configuration(const QHttp1Configuration &other);
+ QHttp1Configuration(QHttp1Configuration &&other) noexcept
+ : u{other.u} { other.u.d = nullptr; }
+
+ Q_NETWORK_EXPORT QHttp1Configuration &operator=(const QHttp1Configuration &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttp1Configuration)
+
+ Q_NETWORK_EXPORT ~QHttp1Configuration();
+
+ Q_NETWORK_EXPORT void setNumberOfConnectionsPerHost(qsizetype amount);
+ Q_NETWORK_EXPORT qsizetype numberOfConnectionsPerHost() const;
+
+ void swap(QHttp1Configuration &other) noexcept
+ { std::swap(u, other.u); }
+
+private:
+ struct ShortData {
+ std::uint8_t numConnectionsPerHost;
+ char reserved[sizeof(void*) - sizeof(numConnectionsPerHost)];
+ };
+ union U {
+ U(ShortData _data) : data(_data) {}
+ QHttp1ConfigurationPrivate *d;
+ ShortData data;
+ } u;
+
+ Q_NETWORK_EXPORT bool equals(const QHttp1Configuration &other) const noexcept;
+ Q_NETWORK_EXPORT size_t hash(size_t seed) const noexcept;
+
+ friend bool operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return lhs.equals(rhs); }
+ friend bool operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return !lhs.equals(rhs); }
+
+ friend size_t qHash(const QHttp1Configuration &key, size_t seed = 0) noexcept { return key.hash(seed); }
+};
+
+Q_DECLARE_SHARED(QHttp1Configuration)
+
+QT_END_NAMESPACE
+
+#endif // QHTTP1CONFIGURATION_H
diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp
index bd4318d4e9..b718ddc755 100644
--- a/src/network/access/qhttp2configuration.cpp
+++ b/src/network/access/qhttp2configuration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttp2configuration.h"
@@ -98,9 +62,6 @@ class QHttp2ConfigurationPrivate : public QSharedData
{
public:
unsigned sessionWindowSize = Http2::defaultSessionWindowSize;
- // The size below is quite a limiting default value, QNetworkRequest
- // by default sets a larger number, an application can change this using
- // QNetworkRequest::setHttp2Configuration.
unsigned streamWindowSize = Http2::defaultSessionWindowSize;
unsigned maxFrameSize = Http2::minPayloadLimit; // Initial (default) value of 16Kb.
@@ -141,12 +102,12 @@ QHttp2Configuration::QHttp2Configuration(QHttp2Configuration &&other) noexcept
}
/*!
- Copy-assigns to this QHttp2Configuration.
+ Copy-assigns \a other to this QHttp2Configuration.
*/
QHttp2Configuration &QHttp2Configuration::operator=(const QHttp2Configuration &) = default;
/*!
- Move-assigns to this QHttp2Configuration.
+ Move-assigns \a other to this QHttp2Configuration.
*/
QHttp2Configuration &QHttp2Configuration::operator=(QHttp2Configuration &&) noexcept = default;
@@ -159,7 +120,7 @@ QHttp2Configuration::~QHttp2Configuration()
/*!
If \a enable is \c true, a remote server can potentially
- use server push to send reponses in advance.
+ use server push to send responses in advance.
\sa serverPushEnabled
*/
@@ -209,6 +170,8 @@ bool QHttp2Configuration::huffmanCompressionEnabled() const
Sets the window size for connection-level flow control.
\a size cannot be 0 and must not exceed 2147483647 octets.
+ Returns \c true on success, \c false otherwise.
+
\sa sessionReceiveWindowSize
*/
bool QHttp2Configuration::setSessionReceiveWindowSize(unsigned size)
@@ -236,6 +199,8 @@ unsigned QHttp2Configuration::sessionReceiveWindowSize() const
Sets the window size for stream-level flow control.
\a size cannot be 0 and must not exceed 2147483647 octets.
+ Returns \c true on success, \c false otherwise.
+
\sa streamReceiveWindowSize
*/
bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
@@ -252,7 +217,7 @@ bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
/*!
Returns the window size for stream-level flow control.
The default value QNetworkAccessManager will be using is
- 21474836 octets.
+ 214748364 octets (see \l {https://httpwg.org/specs/rfc7540.html#SettingValues}{RFC 7540}).
*/
unsigned QHttp2Configuration::streamReceiveWindowSize() const
{
@@ -265,6 +230,8 @@ unsigned QHttp2Configuration::streamReceiveWindowSize() const
\note While this \a size is required to be within a range between
16384 and 16777215 inclusive, the actual payload size in frames
that carry payload maybe be less than 16384.
+
+ Returns \c true on success, \c false otherwise.
*/
bool QHttp2Configuration::setMaxFrameSize(unsigned size)
{
@@ -278,7 +245,7 @@ bool QHttp2Configuration::setMaxFrameSize(unsigned size)
}
/*!
- The maximum payload size that HTTP/2 frames can
+ Returns the maximum payload size that HTTP/2 frames can
have. The default (initial) value is 16384 octets.
*/
unsigned QHttp2Configuration::maxFrameSize() const
@@ -295,18 +262,29 @@ void QHttp2Configuration::swap(QHttp2Configuration &other) noexcept
}
/*!
+ \fn bool QHttp2Configuration::operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) noexcept
Returns \c true if \a lhs and \a rhs have the same set of HTTP/2
parameters.
*/
-bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs)
+
+/*!
+ \fn bool QHttp2Configuration::operator!=(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) noexcept
+ Returns \c true if \a lhs and \a rhs do not have the same set of HTTP/2
+ parameters.
+*/
+
+/*!
+ \internal
+*/
+bool QHttp2Configuration::isEqual(const QHttp2Configuration &other) const noexcept
{
- if (lhs.d == rhs.d)
+ if (d == other.d)
return true;
- return lhs.d->pushEnabled == rhs.d->pushEnabled
- && lhs.d->huffmanCompressionEnabled == rhs.d->huffmanCompressionEnabled
- && lhs.d->sessionWindowSize == rhs.d->sessionWindowSize
- && lhs.d->streamWindowSize == rhs.d->streamWindowSize;
+ return d->pushEnabled == other.d->pushEnabled
+ && d->huffmanCompressionEnabled == other.d->huffmanCompressionEnabled
+ && d->sessionWindowSize == other.d->sessionWindowSize
+ && d->streamWindowSize == other.d->streamWindowSize;
}
QT_END_NAMESPACE
diff --git a/src/network/access/qhttp2configuration.h b/src/network/access/qhttp2configuration.h
index e5c235e2be..ae08b664d4 100644
--- a/src/network/access/qhttp2configuration.h
+++ b/src/network/access/qhttp2configuration.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTP2CONFIGURATION_H
#define QHTTP2CONFIGURATION_H
@@ -44,17 +8,13 @@
#include <QtCore/qshareddata.h>
-#ifndef Q_CLANG_QDOC
QT_REQUIRE_CONFIG(http);
-#endif
QT_BEGIN_NAMESPACE
class QHttp2ConfigurationPrivate;
class Q_NETWORK_EXPORT QHttp2Configuration
{
- friend Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs);
-
public:
QHttp2Configuration();
QHttp2Configuration(const QHttp2Configuration &other);
@@ -82,18 +42,18 @@ public:
void swap(QHttp2Configuration &other) noexcept;
private:
-
QSharedDataPointer<QHttp2ConfigurationPrivate> d;
-};
-Q_DECLARE_SHARED(QHttp2Configuration)
+ bool isEqual(const QHttp2Configuration &other) const noexcept;
-Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs);
+ friend bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) noexcept
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) noexcept
+ { return !lhs.isEqual(rhs); }
-inline bool operator!=(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs)
-{
- return !(lhs == rhs);
-}
+};
+
+Q_DECLARE_SHARED(QHttp2Configuration)
QT_END_NAMESPACE
diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp
new file mode 100644
index 0000000000..8560e0da38
--- /dev/null
+++ b/src/network/access/qhttp2connection.cpp
@@ -0,0 +1,1752 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp2connection_p.h"
+
+#include <private/bitstreams_p.h>
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/private/qiodevice_p.h>
+#include <QtCore/private/qnoncontiguousbytedevice_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/QRandomGenerator>
+#include <QtCore/qloggingcategory.h>
+
+#include <algorithm>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qHttp2ConnectionLog, "qt.network.http2.connection", QtCriticalMsg)
+
+using namespace Qt::StringLiterals;
+using namespace Http2;
+
+/*!
+ \class QHttp2Stream
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Stream class represents a single HTTP/2 stream.
+ Must be created by QHttp2Connection.
+
+ \sa QHttp2Connection
+*/
+
+QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept
+ : QObject(connection), m_streamID(streamID)
+{
+ Q_ASSERT(connection);
+ Q_ASSERT(streamID); // stream id 0 is reserved for connection control messages
+ qCDebug(qHttp2ConnectionLog, "[%p] new stream %u", connection, streamID);
+}
+
+QHttp2Stream::~QHttp2Stream() noexcept = default;
+
+/*!
+ \fn quint32 QHttp2Stream::streamID() const noexcept
+
+ Returns the stream ID of this stream.
+*/
+
+/*!
+ \fn void QHttp2Stream::headersReceived(const HPack::HttpHeader &headers, bool endStream)
+
+ This signal is emitted when the remote peer has sent a HEADERS frame, and
+ potentially some CONTINUATION frames, ending with the END_HEADERS flag
+ to this stream.
+
+ The headers are internally combined and decompressed, and are accessible
+ through the \a headers parameter. If the END_STREAM flag was set, the
+ \a endStream parameter will be \c true, indicating that the peer does not
+ intend to send any more frames on this stream.
+
+ \sa receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::headersUpdated()
+
+ This signal may be emitted if a new HEADERS frame was received after
+ already processing a previous HEADERS frame.
+
+ \sa headersReceived(), receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::errorOccurred(quint32 errorCode, const QString &errorString)
+
+ This signal is emitted when the stream has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+
+ \sa https://www.rfc-editor.org/rfc/rfc7540#section-7
+*/
+
+/*!
+ \fn void QHttp2Stream::stateChanged(State newState)
+
+ This signal is emitted when the state of the stream changes. The \a newState
+ parameter is the new state of the stream.
+
+ Examples of this is sending or receiving a frame with the END_STREAM flag.
+ This will transition the stream to the HalfClosedLocal or HalfClosedRemote
+ state, respectively.
+
+ \sa state()
+*/
+
+
+/*!
+ \fn void QHttp2Stream::promisedStreamReceived(quint32 newStreamID)
+
+ This signal is emitted when the remote peer has promised a new stream with
+ the given \a newStreamID.
+
+ \sa QHttp2Connection::promisedStream()
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadBlocked()
+
+ This signal is emitted when the stream is unable to send more data because
+ the remote peer's receive window is full.
+
+ This is mostly intended for diagnostics as there is no expectation that the
+ user can do anything to react to this.
+*/
+
+/*!
+ \fn void QHttp2Stream::dataReceived(const QByteArray &data, bool endStream)
+
+ This signal is emitted when the stream has received a DATA frame from the
+ remote peer. The \a data parameter contains the payload of the frame, and
+ the \a endStream parameter is \c true if the END_STREAM flag was set.
+
+ \sa downloadBuffer()
+*/
+
+/*!
+ \fn void QHttp2Stream::bytesWritten(qint64 bytesWritten)
+
+ This signal is emitted when the stream has written \a bytesWritten bytes to
+ the network.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadDeviceError(const QString &errorString)
+
+ This signal is emitted if the upload device encounters an error while
+ sending data. The \a errorString parameter is a human-readable description
+ of the error.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadFinished()
+
+ This signal is emitted when the stream has finished sending all the data
+ from the upload device.
+
+ If the END_STREAM flag was set for sendData() then the stream will be
+ closed for further writes before this signal is emitted.
+*/
+
+/*!
+ \fn bool QHttp2Stream::isUploadingDATA() const noexcept
+
+ Returns \c true if the stream is currently sending DATA frames.
+*/
+
+/*!
+ \fn State QHttp2Stream::state() const noexcept
+
+ Returns the current state of the stream.
+
+ \sa stateChanged()
+*/
+/*!
+ \fn bool QHttp2Stream::isActive() const noexcept
+
+ Returns \c true if the stream has been opened and is not yet closed.
+*/
+/*!
+ \fn bool QHttp2Stream::isPromisedStream() const noexcept
+
+ Returns \c true if the stream was promised by the remote peer.
+*/
+/*!
+ \fn bool QHttp2Stream::wasReset() const noexcept
+
+ Returns \c true if the stream was reset by the remote peer.
+*/
+/*!
+ \fn quint32 QHttp2Stream::RST_STREAM_code() const noexcept
+
+ Returns the HTTP/2 error code if the stream was reset by the remote peer.
+ If the stream was not reset, this function returns 0.
+*/
+/*!
+ \fn HPack::HttpHeader QHttp2Stream::receivedHeaders() const noexcept
+
+ Returns the headers received from the remote peer, if any.
+*/
+/*!
+ \fn QByteDataBuffer QHttp2Stream::downloadBuffer() const noexcept
+
+ Returns the buffer containing the data received from the remote peer.
+*/
+
+void QHttp2Stream::finishWithError(quint32 errorCode, const QString &message)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u finished with error: %ls (error code: %u)",
+ getConnection(), m_streamID, qUtf16Printable(message), errorCode);
+ transitionState(StateTransition::RST);
+ emit errorOccurred(errorCode, message);
+}
+
+void QHttp2Stream::finishWithError(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ finishWithError(error, message);
+}
+
+/*!
+ Sends a RST_STREAM frame with the given \a errorCode.
+ This closes the stream for both sides, any further frames will be dropped.
+
+ Returns \c false if the stream is closed or idle, also if it fails to send
+ the RST_STREAM frame. Otherwise, returns \c true.
+*/
+bool QHttp2Stream::sendRST_STREAM(quint32 errorCode)
+{
+ if (m_state == State::Closed || m_state == State::Idle)
+ return false;
+ qCDebug(qHttp2ConnectionLog, "[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
+ m_streamID, errorCode);
+ transitionState(StateTransition::RST);
+
+ QHttp2Connection *connection = getConnection();
+ FrameWriter &frameWriter = connection->frameWriter;
+ frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
+ frameWriter.append(errorCode);
+ return frameWriter.write(*connection->getSocket());
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QIODevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadDevice);
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
+ connect(this, &QHttp2Stream::uploadFinished, byteDevice, &QObject::deleteLater);
+ byteDevice->setParent(this);
+ m_uploadDevice = device;
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of IODevice: %p",
+ getConnection(), m_streamID, device);
+ sendDATA(byteDevice, endStream);
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QNonContiguousByteDevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of device: %p",
+ getConnection(), m_streamID, device);
+
+ m_uploadByteDevice = device;
+ m_endStreamAfterDATA = endStream;
+ connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead, this,
+ &QHttp2Stream::maybeResumeUpload);
+ connect(m_uploadByteDevice, &QObject::destroyed, this, &QHttp2Stream::uploadDeviceDestroyed);
+
+ internalSendDATA();
+}
+
+void QHttp2Stream::internalSendDATA()
+{
+ Q_ASSERT(m_uploadByteDevice);
+ QHttp2Connection *connection = getConnection();
+ Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
+ QIODevice *socket = connection->getSocket();
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, about to write to socket, current session window size: %d, stream "
+ "window size: %d, bytes available: %lld",
+ connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
+ m_uploadByteDevice->size() - m_uploadByteDevice->pos());
+
+ qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
+ FrameWriter &frameWriter = connection->frameWriter;
+ qint64 totalBytesWritten = 0;
+ const auto deviceCanRead = [this, connection] {
+ // We take advantage of knowing the internals of one of the devices used.
+ // It will request X bytes to move over to the http thread if there's
+ // not enough left, so we give it a large size. It will anyway return
+ // the size it can actually provide.
+ const qint64 requestSize = connection->maxFrameSize * 10ll;
+ qint64 tmp = 0;
+ return m_uploadByteDevice->readPointer(requestSize, tmp) != nullptr && tmp > 0;
+ };
+
+ bool sentEND_STREAM = false;
+ while (remainingWindowSize && deviceCanRead()) {
+ quint32 bytesWritten = 0;
+ qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
+ frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
+
+ while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
+ const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
+
+ qint64 outBytesAvail = 0;
+ const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
+ if (!readPointer || outBytesAvail <= 0) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
+ connection, m_streamID, m_uploadByteDevice, outBytesAvail);
+ break;
+ }
+ const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
+ frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
+ m_uploadByteDevice->advanceReadPointer(bytesToWrite);
+
+ bytesWritten += bytesToWrite;
+
+ m_sendWindow -= bytesToWrite;
+ Q_ASSERT(m_sendWindow >= 0);
+ connection->sessionSendWindowSize -= bytesToWrite;
+ Q_ASSERT(connection->sessionSendWindowSize >= 0);
+ remainingBytesInFrame -= bytesToWrite;
+ Q_ASSERT(remainingBytesInFrame >= 0);
+ remainingWindowSize -= bytesToWrite;
+ Q_ASSERT(remainingWindowSize >= 0);
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, writing %u bytes to socket", connection,
+ m_streamID, bytesWritten);
+ if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
+ sentEND_STREAM = true;
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+ }
+ if (!frameWriter.write(*socket)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, failed to write to socket", connection,
+ m_streamID);
+ finishWithError(QNetworkReply::ProtocolFailure, "failed to write to socket"_L1);
+ return;
+ }
+
+ totalBytesWritten += bytesWritten;
+ }
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
+ "more later. Remaining window size: %d",
+ connection, m_streamID, totalBytesWritten, remainingWindowSize);
+
+ emit bytesWritten(totalBytesWritten);
+ if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
+ "after DATA",
+ connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
+ m_endStreamAfterDATA ? "" : "not ");
+ if (!sentEND_STREAM && m_endStreamAfterDATA) {
+ // We need to send an empty DATA frame with END_STREAM since we
+ // have exhausted the device, but we haven't sent END_STREAM yet.
+ // This can happen if we got a final readyRead to signify no more
+ // data available, but we hadn't sent the END_STREAM flag yet.
+ frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
+ frameWriter.write(*socket);
+ }
+ finishSendDATA();
+ } else if (isUploadBlocked()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, upload blocked", connection, m_streamID);
+ emit uploadBlocked();
+ }
+}
+
+void QHttp2Stream::finishSendDATA()
+{
+ if (m_endStreamAfterDATA)
+ transitionState(StateTransition::CloseLocal);
+
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ emit uploadFinished();
+}
+
+void QHttp2Stream::maybeResumeUpload()
+{
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
+ "%d",
+ getConnection(), m_streamID, m_uploadByteDevice,
+ !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
+ isUploadBlocked());
+ if (isUploadingDATA() && !isUploadBlocked())
+ internalSendDATA();
+}
+
+/*!
+ Returns \c true if the stream is currently unable to send more data because
+ the remote peer's receive window is full.
+*/
+bool QHttp2Stream::isUploadBlocked() const noexcept
+{
+ constexpr auto MinFrameSize = Http2::frameHeaderSize + 1; // 1 byte payload
+ return isUploadingDATA()
+ && (m_sendWindow <= MinFrameSize
+ || getConnection()->sessionSendWindowSize <= MinFrameSize);
+}
+
+void QHttp2Stream::uploadDeviceReadChannelFinished()
+{
+ maybeResumeUpload();
+}
+
+/*!
+ Sends a HEADERS frame with the given \a headers and \a priority.
+ If \a endStream is \c true, the END_STREAM flag will be set, and the stream
+ will be closed for future writes.
+ If the headers are too large, or the stream is not in the correct state,
+ this function will return \c false. Otherwise, it will return \c true.
+*/
+bool QHttp2Stream::sendHEADERS(const HPack::HttpHeader &headers, bool endStream, quint8 priority)
+{
+ using namespace HPack;
+ if (auto hs = header_size(headers);
+ !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
+ return false;
+ }
+
+ transitionState(StateTransition::Open);
+
+ Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
+
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, sending HEADERS frame with %u entries",
+ connection, streamID(), uint(headers.size()));
+
+ QIODevice *socket = connection->getSocket();
+ FrameWriter &frameWriter = connection->frameWriter;
+
+ frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
+ if (endStream)
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+
+ frameWriter.append(quint32()); // No stream dependency in Qt.
+ frameWriter.append(priority);
+
+ // Compress in-place:
+ BitOStream outputStream(frameWriter.outboundFrame().buffer);
+ if (connection->m_connectionType == QHttp2Connection::Type::Client) {
+ if (!connection->encoder.encodeRequest(outputStream, headers))
+ return false;
+ } else {
+ if (!connection->encoder.encodeResponse(outputStream, headers))
+ return false;
+ }
+
+ bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
+ if (endStream)
+ transitionState(StateTransition::CloseLocal);
+
+ return result;
+}
+
+/*!
+ Sends a WINDOW_UPDATE frame with the given \a delta.
+ This increases our receive window size for this stream, allowing the remote
+ peer to send more data.
+*/
+void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
+{
+ QHttp2Connection *connection = getConnection();
+ m_recvWindow += qint32(delta);
+ connection->sendWINDOW_UPDATE(streamID(), delta);
+}
+
+void QHttp2Stream::uploadDeviceDestroyed()
+{
+ if (isUploadingDATA()) {
+ // We're in the middle of sending DATA frames, we need to abort
+ // the stream.
+ sendRST_STREAM(CANCEL);
+ emit uploadDeviceError("Upload device destroyed while uploading"_L1);
+ }
+ m_uploadDevice = nullptr;
+}
+
+void QHttp2Stream::setState(State newState)
+{
+ if (m_state == newState)
+ return;
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, state changed from %d to %d", getConnection(),
+ streamID(), int(m_state), int(newState));
+ m_state = newState;
+ emit stateChanged(newState);
+}
+
+// Changes the state as appropriate given the current state and the transition.
+// Always call this before emitting any signals since the recipient might rely
+// on the new state!
+void QHttp2Stream::transitionState(StateTransition transition)
+{
+ switch (m_state) {
+ case State::Idle:
+ if (transition == StateTransition::Open)
+ setState(State::Open);
+ else
+ Q_UNREACHABLE(); // We should transition to Open before ever getting here
+ break;
+ case State::Open:
+ switch (transition) {
+ case StateTransition::CloseLocal:
+ setState(State::HalfClosedLocal);
+ break;
+ case StateTransition::CloseRemote:
+ setState(State::HalfClosedRemote);
+ break;
+ case StateTransition::RST:
+ setState(State::Closed);
+ break;
+ case StateTransition::Open: // no-op
+ break;
+ }
+ break;
+ case State::HalfClosedLocal:
+ if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::HalfClosedRemote:
+ if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::ReservedRemote:
+ if (transition == StateTransition::RST) {
+ setState(State::Closed);
+ } else if (transition == StateTransition::CloseLocal) { // Receiving HEADER closes local
+ setState(State::HalfClosedLocal);
+ }
+ break;
+ case State::Closed:
+ break;
+ }
+}
+
+void QHttp2Stream::handleDATA(const Frame &inboundFrame)
+{
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, received DATA frame with payload of %u bytes",
+ connection, m_streamID, inboundFrame.payloadSize());
+
+ if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
+ finishWithError(QNetworkReply::ProtocolFailure, "flow control error"_L1);
+ sendRST_STREAM(FLOW_CONTROL_ERROR);
+ return;
+ }
+ m_recvWindow -= qint32(inboundFrame.payloadSize());
+ const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
+ // Uncompress data if needed and append it ...
+ if (inboundFrame.dataSize() > 0 || endStream) {
+ QByteArray fragment(reinterpret_cast<const char *>(inboundFrame.dataBegin()),
+ inboundFrame.dataSize());
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ emit dataReceived(fragment, endStream);
+ m_downloadBuffer.append(std::move(fragment));
+ }
+
+ if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
+ }
+}
+
+void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers)
+{
+ if (m_state == State::Idle)
+ transitionState(StateTransition::Open);
+ const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ if (!headers.empty()) {
+ m_headers.insert(m_headers.end(), headers.begin(), headers.end());
+ emit headersUpdated();
+ }
+ emit headersReceived(headers, endStream);
+}
+
+void QHttp2Stream::handleRST_STREAM(const Frame &inboundFrame)
+{
+ transitionState(StateTransition::RST);
+ m_RST_STREAM_code = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if (isUploadingDATA()) {
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ }
+ finishWithError(*m_RST_STREAM_code, ""_L1);
+}
+
+void QHttp2Stream::handleWINDOW_UPDATE(const Frame &inboundFrame)
+{
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
+ "PROTOCOL_ERROR",
+ getConnection(), m_streamID, delta);
+ finishWithError(QNetworkReply::ProtocolFailure, "invalid WINDOW_UPDATE delta"_L1);
+ sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+ m_sendWindow = sum;
+ // Stream may have been unblocked, so maybe try to write again
+ if (isUploadingDATA())
+ maybeResumeUpload();
+}
+
+/*!
+ \class QHttp2Connection
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Connection class represents a HTTP/2 connection.
+ It can only be created through the static functions
+ createDirectConnection(), createUpgradedConnection(),
+ and createDirectServerConnection().
+
+ createDirectServerConnection() is used for server-side connections, and has
+ certain limitations that a client does not.
+
+ As a client you can create a QHttp2Stream with createStream().
+
+ \sa QHttp2Stream
+*/
+
+/*!
+ \fn void QHttp2Connection::newIncomingStream(QHttp2Stream *stream)
+
+ This signal is emitted when a new \a stream is received from the remote
+ peer.
+*/
+
+/*!
+ \fn void QHttp2Connection::newPromisedStream(QHttp2Stream *stream)
+
+ This signal is emitted when the remote peer has promised a new \a stream.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorReceived()
+
+ This signal is emitted when the connection has received an error.
+*/
+
+/*!
+ \fn void QHttp2Connection::connectionClosed()
+
+ This signal is emitted when the connection has been closed.
+*/
+
+/*!
+ \fn void QHttp2Connection::settingsFrameReceived()
+
+ This signal is emitted when the connection has received a SETTINGS frame.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorOccurred(Http2::Http2Error errorCode, const QString &errorString)
+
+ This signal is emitted when the connection has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+*/
+
+/*!
+ \fn void QHttp2Connection::receivedGOAWAY(quint32 errorCode, quint32 lastStreamID)
+
+ This signal is emitted when the connection has received a GOAWAY frame. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a lastStreamID
+ parameter is the last stream ID that the remote peer will process.
+
+ Any streams of a higher stream ID created by us will be ignored or reset.
+*/
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function assumes that the Upgrade headers etc. in http/1 have already
+ been sent and that the connection is already upgraded to http/2.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ Q_ASSERT(socket);
+
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+ // HTTP2 connection is already established and request was sent, so stream 1
+ // is already 'active' and is closed for any further outgoing data.
+ QHttp2Stream *stream = connection->createStreamInternal().unwrap();
+ Q_ASSERT(stream->streamID() == 1);
+ stream->setState(QHttp2Stream::State::HalfClosedLocal);
+ connection->m_upgradedConnection = true;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function will immediately send the client preface.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Server;
+
+ connection->m_nextStreamID = 2; // server-initiated streams must be even
+
+ connection->m_waitingForClientPreface = true;
+
+ return connection.release();
+}
+
+/*!
+ Creates a stream on this connection.
+
+ Automatically picks the next available stream ID and returns a pointer to
+ the new stream, if possible. Otherwise returns an error.
+
+ \sa QHttp2Connection::CreateStreamError, QHttp2Stream
+*/
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream()
+{
+ Q_ASSERT(m_connectionType == Type::Client); // This overload is just for clients
+ if (m_nextStreamID > lastValidStreamID)
+ return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
+ return createStreamInternal();
+}
+
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
+QHttp2Connection::createStreamInternal()
+{
+ if (m_goingAway)
+ return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
+ const quint32 streamID = m_nextStreamID;
+ if (size_t(m_maxConcurrentStreams) <= size_t(numActiveLocalStreams()))
+ return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
+ m_nextStreamID += 2;
+ return { createStreamInternal_impl(streamID) };
+}
+
+QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID)
+{
+ qsizetype numStreams = m_streams.size();
+ QPointer<QHttp2Stream> &stream = m_streams[streamID];
+ if (numStreams == m_streams.size()) // stream already existed
+ return nullptr;
+ stream = new QHttp2Stream(this, streamID);
+ stream->m_recvWindow = streamInitialReceiveWindowSize;
+ stream->m_sendWindow = streamInitialSendWindowSize;
+ return stream;
+}
+
+qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask) const noexcept
+{
+ const auto shouldCount = [mask](const QPointer<QHttp2Stream> &stream) -> bool {
+ return stream && (stream->streamID() & 1) == mask;
+ };
+ return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
+}
+
+/*!
+ \internal
+ The number of streams the remote peer has started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveRemoteStreams() const noexcept
+{
+ const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
+ return numActiveStreamsImpl(RemoteMask);
+}
+
+/*!
+ \internal
+ The number of streams we have started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveLocalStreams() const noexcept
+{
+ const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
+ return numActiveStreamsImpl(LocalMask);
+}
+
+/*!
+ Return a pointer to a stream with the given \a streamID, or null if no such
+ stream exists or it was deleted.
+*/
+QHttp2Stream *QHttp2Connection::getStream(quint32 streamID) const
+{
+ return m_streams.value(streamID, nullptr).get();
+}
+
+
+/*!
+ \fn QHttp2Stream *QHttp2Connection::promisedStream(const QUrl &streamKey) const
+
+ Returns a pointer to the stream that was promised with the given
+ \a streamKey, if any. Otherwise, returns null.
+*/
+
+/*!
+ \fn void QHttp2Connection::close()
+
+ This sends a GOAWAY frame on the connection stream, gracefully closing the
+ connection.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isGoingAway() const noexcept
+
+ Returns \c true if the connection is in the process of being closed, or
+ \c false otherwise.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxConcurrentStreams() const noexcept
+
+ Returns the maximum number of concurrent streams we are allowed to have
+ active at any given time. This is a directional setting, and the remote
+ peer may have a different value.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxHeaderListSize() const noexcept
+
+ Returns the maximum size of the header which the peer is willing to accept.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isUpgradedConnection() const noexcept
+
+ Returns \c true if this connection was created as a result of an HTTP/1
+ upgrade to HTTP/2, or \c false otherwise.
+*/
+
+QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(socket->isOpen());
+ Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
+ // We don't make any connections directly because this is used in
+ // in the http2 protocol handler, which is used by
+ // QHttpNetworkConnectionChannel. Which in turn owns and deals with all the
+ // socket connections.
+}
+
+QHttp2Connection::~QHttp2Connection() noexcept
+{
+ // delete streams now so that any calls it might make back to this
+ // Connection will operate on a valid object.
+ for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
+ delete stream.get();
+}
+
+bool QHttp2Connection::serverCheckClientPreface()
+{
+ if (!m_waitingForClientPreface)
+ return true;
+ auto *socket = getSocket();
+ if (socket->bytesAvailable() < Http2::clientPrefaceLength)
+ return false;
+ if (!readClientPreface()) {
+ socket->close();
+ emit errorOccurred(Http2Error::PROTOCOL_ERROR, "invalid client preface"_L1);
+ qCDebug(qHttp2ConnectionLog, "[%p] Invalid client preface", this);
+ return false;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Peer sent valid client preface", this);
+ m_waitingForClientPreface = false;
+ if (!sendServerPreface()) {
+ connectionError(Http2::INTERNAL_ERROR, "Failed to send server preface");
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendPing()
+{
+ std::array<char, 8> data;
+
+ QRandomGenerator gen;
+ gen.generate(data.begin(), data.end());
+ return sendPing(data);
+}
+
+bool QHttp2Connection::sendPing(QByteArrayView data)
+{
+ frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
+
+ Q_ASSERT(data.length() == 8);
+ if (!m_lastPingSignature) {
+ m_lastPingSignature = data.toByteArray();
+ } else {
+ qCWarning(qHttp2ConnectionLog, "[%p] No PING is sent while waiting for the previous PING.", this);
+ return false;
+ }
+
+ frameWriter.append((uchar*)data.data(), (uchar*)data.end());
+ frameWriter.write(*getSocket());
+ return true;
+}
+
+/*!
+ This function must be called when you have received a readyRead signal
+ (or equivalent) from the QIODevice. It will read and process any incoming
+ HTTP/2 frames and emit signals as appropriate.
+*/
+void QHttp2Connection::handleReadyRead()
+{
+ /* event loop */
+ if (m_connectionType == Type::Server && !serverCheckClientPreface())
+ return;
+
+ const auto streamIsActive = [](const QPointer<QHttp2Stream> &stream) {
+ return stream && stream->isActive();
+ };
+ if (m_goingAway && std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ close();
+ return;
+ }
+ QIODevice *socket = getSocket();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Receiving data, %lld bytes available", this,
+ socket->bytesAvailable());
+
+ using namespace Http2;
+ while (!m_goingAway || std::any_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ const auto result = frameReader.read(*socket);
+ if (result != FrameStatus::goodFrame)
+ qCDebug(qHttp2ConnectionLog, "[%p] Tried to read frame, got %d", this, int(result));
+ switch (result) {
+ case FrameStatus::incompleteFrame:
+ return;
+ case FrameStatus::protocolError:
+ return connectionError(PROTOCOL_ERROR, "invalid frame");
+ case FrameStatus::sizeError:
+ return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
+ default:
+ break;
+ }
+
+ Q_ASSERT(result == FrameStatus::goodFrame);
+
+ inboundFrame = std::move(frameReader.inboundFrame());
+
+ const auto frameType = inboundFrame.type();
+ qCDebug(qHttp2ConnectionLog, "[%p] Successfully read a frame, with type: %d", this,
+ int(frameType));
+ if (continuationExpected && frameType != FrameType::CONTINUATION)
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
+
+ switch (frameType) {
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::HEADERS:
+ handleHEADERS();
+ break;
+ case FrameType::PRIORITY:
+ handlePRIORITY();
+ break;
+ case FrameType::RST_STREAM:
+ handleRST_STREAM();
+ break;
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::PUSH_PROMISE:
+ handlePUSH_PROMISE();
+ break;
+ case FrameType::PING:
+ handlePING();
+ break;
+ case FrameType::GOAWAY:
+ handleGOAWAY();
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ case FrameType::CONTINUATION:
+ handleCONTINUATION();
+ break;
+ case FrameType::LAST_FRAME_TYPE:
+ // 5.1 - ignore unknown frames.
+ break;
+ }
+ }
+}
+
+bool QHttp2Connection::readClientPreface()
+{
+ auto *socket = getSocket();
+ Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
+ char buffer[Http2::clientPrefaceLength];
+ const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
+ if (read != Http2::clientPrefaceLength)
+ return false;
+ return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
+}
+
+/*!
+ This function must be called when the socket has been disconnected, and will
+ end all remaining streams with an error.
+*/
+void QHttp2Connection::handleConnectionClosure()
+{
+ const auto errorString = QCoreApplication::translate("QHttp", "Connection closed");
+ for (auto it = m_streams.begin(), end = m_streams.end(); it != end; ++it) {
+ auto stream = it.value();
+ if (stream && stream->isActive())
+ stream->finishWithError(QNetworkReply::RemoteHostClosedError, errorString);
+ }
+}
+
+void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
+{
+ m_config = std::move(config);
+
+ // These values comes from our own API so trust it to be sane.
+ maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
+ pushPromiseEnabled = m_config.serverPushEnabled();
+ streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
+ encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
+}
+
+void QHttp2Connection::connectionError(Http2Error errorCode, const char *message)
+{
+ Q_ASSERT(message);
+ if (m_goingAway)
+ return;
+
+ qCCritical(qHttp2ConnectionLog, "[%p] Connection error: %s (%d)", this, message,
+ int(errorCode));
+
+ m_goingAway = true;
+ sendGOAWAY(errorCode);
+ const auto error = qt_error(errorCode);
+ auto messageView = QLatin1StringView(message);
+
+ for (QHttp2Stream *stream : std::as_const(m_streams)) {
+ if (stream && stream->isActive())
+ stream->finishWithError(error, messageView);
+ }
+
+ closeSession();
+}
+
+void QHttp2Connection::closeSession()
+{
+ emit connectionClosed();
+}
+
+bool QHttp2Connection::streamWasReset(quint32 streamID) noexcept
+{
+ return m_resetStreamIDs.contains(streamID);
+}
+
+bool QHttp2Connection::isInvalidStream(quint32 streamID) noexcept
+{
+ auto stream = m_streams.value(streamID, nullptr);
+ return !stream && !streamWasReset(streamID);
+}
+
+bool QHttp2Connection::sendClientPreface()
+{
+ QIODevice *socket = getSocket();
+ // 3.5 HTTP/2 Connection Preface
+ const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
+ if (written != clientPrefaceLength)
+ return false;
+
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendServerPreface()
+{
+ // We send our SETTINGS frame and ACK the client's SETTINGS frame when it
+ // arrives.
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendSETTINGS()
+{
+ QIODevice *socket = getSocket();
+ // 6.5 SETTINGS
+ frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS frame, %d bytes", this,
+ frameWriter.outboundFrame().payloadSize());
+ Q_ASSERT(frameWriter.outboundFrame().payloadSize());
+
+ if (!frameWriter.write(*socket))
+ return false;
+
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ // We only send WINDOW_UPDATE for the connection if the size differs from the
+ // default 64 KB:
+ const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
+ if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
+ return false;
+
+ waitingForSettingsACK = true;
+ return true;
+}
+
+bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u", this,
+ streamID, delta);
+ frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ frameWriter.append(delta);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendGOAWAY(quint32 errorCode)
+{
+ frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
+ Http2PredefinedParameters::connectionStreamID);
+ frameWriter.append(quint32(m_lastIncomingStreamID));
+ frameWriter.append(errorCode);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendSETTINGS_ACK()
+{
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
+ return frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleDATA()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "DATA on the connection stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
+
+ if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] Received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
+ return connectionError(FLOW_CONTROL_ERROR, "Flow control error");
+ }
+
+ sessionReceiveWindowSize -= inboundFrame.payloadSize();
+
+ auto it = m_streams.constFind(streamID);
+ if (it != m_streams.cend() && it.value())
+ it.value()->handleDATA(inboundFrame);
+
+ if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ QMetaObject::invokeMethod(this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
+ quint32(connectionStreamID),
+ quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ }
+}
+
+void QHttp2Connection::handleHEADERS()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS frame on stream %d", this, streamID);
+
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "HEADERS on 0x0 stream");
+
+ const bool isClient = m_connectionType == Type::Client;
+ const bool isClientInitiatedStream = !!(streamID & 1);
+ const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
+
+ if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
+ QHttp2Stream *newStream = createStreamInternal_impl(streamID);
+ Q_ASSERT(newStream);
+ m_lastIncomingStreamID = streamID;
+ qCDebug(qHttp2ConnectionLog, "[%p] Created new incoming stream %d", this, streamID);
+ emit newIncomingStream(newStream);
+ } else if (auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on non-existent stream %d", this,
+ streamID);
+ return connectionError(PROTOCOL_ERROR, "HEADERS on invalid stream");
+ } else if (!*it || (*it)->wasReset()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on reset stream %d", this, streamID);
+ return connectionError(ENHANCE_YOUR_CALM, "HEADERS on invalid stream");
+ }
+
+ const auto flags = inboundFrame.flags();
+ if (flags.testFlag(FrameFlag::PRIORITY)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] HEADERS frame on stream %d has PRIORITY flag", this,
+ streamID);
+ handlePRIORITY();
+ if (m_goingAway)
+ return;
+ }
+
+ const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePRIORITY()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
+ || inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PRIORITY on 0x0 stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "PRIORITY on invalid stream");
+
+ quint32 streamDependency = 0;
+ uchar weight = 0;
+ const bool noErr = inboundFrame.priority(&streamDependency, &weight);
+ Q_UNUSED(noErr);
+ Q_ASSERT(noErr);
+
+ const bool exclusive = streamDependency & 0x80000000;
+ streamDependency &= ~0x80000000;
+
+ // Ignore this for now ...
+ // Can be used for streams (re)prioritization - 5.3
+ Q_UNUSED(exclusive);
+ Q_UNUSED(weight);
+}
+
+void QHttp2Connection::handleRST_STREAM()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
+
+ // "RST_STREAM frames MUST be associated with a stream.
+ // If a RST_STREAM frame is received with a stream identifier of 0x0,
+ // the recipient MUST treat this as a connection error (Section 5.4.1)
+ // of type PROTOCOL_ERROR.
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on 0x0");
+
+ if (!(streamID & 0x1)) { // @future[server]: must be updated for server-side handling
+ // RST_STREAM on a promised stream:
+ // since we do not keep track of such streams,
+ // just ignore.
+ return;
+ }
+
+ // Anything greater than m_nextStreamID has not been started yet.
+ if (streamID >= m_nextStreamID) {
+ // "RST_STREAM frames MUST NOT be sent for a stream
+ // in the "idle" state. .. the recipient MUST treat this
+ // as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on idle stream");
+ }
+
+ Q_ASSERT(inboundFrame.dataSize() == 4);
+
+ if (QPointer<QHttp2Stream> stream = m_streams[streamID])
+ stream->handleRST_STREAM(inboundFrame);
+}
+
+void QHttp2Connection::handleSETTINGS()
+{
+ // 6.5 SETTINGS.
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "SETTINGS on invalid stream");
+
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
+ if (!waitingForSettingsACK)
+ return connectionError(PROTOCOL_ERROR, "unexpected SETTINGS ACK");
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ACK", this);
+ waitingForSettingsACK = false;
+ return;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS frame", this);
+
+ if (inboundFrame.dataSize()) {
+ auto src = inboundFrame.dataBegin();
+ for (const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
+ const Settings identifier = Settings(qFromBigEndian<quint16>(src));
+ const quint32 intVal = qFromBigEndian<quint32>(src + 2);
+ if (!acceptSetting(identifier, intVal)) {
+ // If not accepted - we finish with connectionError.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received an unacceptable setting, %u, %u", this,
+ quint32(identifier), intVal);
+ return; // connectionError already called in acceptSetting.
+ }
+ }
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS ACK", this);
+ emit settingsFrameReceived();
+ sendSETTINGS_ACK();
+}
+
+void QHttp2Connection::handlePUSH_PROMISE()
+{
+ // 6.6 PUSH_PROMISE.
+ Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
+
+ if (!pushPromiseEnabled && !waitingForSettingsACK) {
+ // This means, server ACKed our 'NO PUSH',
+ // but sent us PUSH_PROMISE anyway.
+ return connectionError(PROTOCOL_ERROR, "unexpected PUSH_PROMISE frame");
+ }
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid associated stream (0x0)");
+
+ auto it = m_streams.constFind(streamID);
+#if 0 // Needs to be done after some timeout in case the stream has only just been reset
+ if (it != m_streams.constEnd()) {
+ QHttp2Stream *associatedStream = it->get();
+ if (associatedStream->state() != QHttp2Stream::State::Open
+ && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
+ // Cause us to error out below:
+ it = m_streams.constEnd();
+ }
+ }
+#endif
+ if (it == m_streams.constEnd())
+ return connectionError(ENHANCE_YOUR_CALM, "PUSH_PROMISE with invalid associated stream");
+
+ const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid promised stream ID");
+
+ auto *stream = createStreamInternal_impl(reservedID);
+ if (!stream)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with already active stream ID");
+ m_lastIncomingStreamID = reservedID;
+ stream->setState(QHttp2Stream::State::ReservedRemote);
+
+ if (!pushPromiseEnabled) {
+ // "ignoring a PUSH_PROMISE frame causes the stream state to become
+ // indeterminate" - let's send RST_STREAM frame with REFUSE_STREAM code.
+ stream->sendRST_STREAM(REFUSE_STREAM);
+ }
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePING()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PING);
+ Q_ASSERT(inboundFrame.dataSize() == 8);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
+
+ if (inboundFrame.flags() & FrameFlag::ACK) {
+ QByteArrayView pingSignature(reinterpret_cast<const char *>(inboundFrame.dataBegin()), 8);
+ if (!m_lastPingSignature.has_value()) {
+ emit pingFrameRecived(PingState::PongNoPingSent);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING with ACK received but no PING was sent.", this);
+ } else if (pingSignature != m_lastPingSignature) {
+ emit pingFrameRecived(PingState::PongSignatureChanged);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING signature does not match the last PING.", this);
+ } else {
+ emit pingFrameRecived(PingState::PongSignatureIdentical);
+ }
+ m_lastPingSignature.reset();
+ return;
+ } else {
+ emit pingFrameRecived(PingState::Ping);
+
+ }
+
+
+ frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
+ frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
+ frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleGOAWAY()
+{
+ // 6.8 GOAWAY
+
+ Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
+ // "An endpoint MUST treat a GOAWAY frame with a stream identifier
+ // other than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY on invalid stream");
+
+ const uchar *const src = inboundFrame.dataBegin();
+ quint32 lastStreamID = qFromBigEndian<quint32>(src);
+ const quint32 errorCode = qFromBigEndian<quint32>(src + 4);
+
+ if (!lastStreamID) {
+ // "The last stream identifier can be set to 0 if no
+ // streams were processed."
+ lastStreamID = 1;
+ } else if (!(lastStreamID & 0x1)) {
+ // 5.1.1 - we (client) use only odd numbers as stream identifiers.
+ return connectionError(PROTOCOL_ERROR, "GOAWAY with invalid last stream ID");
+ } else if (lastStreamID >= m_nextStreamID) {
+ // "A server that is attempting to gracefully shut down a connection SHOULD
+ // send an initial GOAWAY frame with the last stream identifier set to 2^31-1
+ // and a NO_ERROR code."
+ if (lastStreamID != lastValidStreamID || errorCode != HTTP2_NO_ERROR)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY invalid stream/error code");
+ } else {
+ lastStreamID += 2;
+ }
+
+ m_goingAway = true;
+
+ emit receivedGOAWAY(errorCode, lastStreamID);
+
+ for (quint32 id = lastStreamID; id < m_nextStreamID; id += 2) {
+ QHttp2Stream *stream = m_streams.value(id, nullptr);
+ if (stream && stream->isActive())
+ stream->finishWithError(errorCode, "Received GOAWAY"_L1);
+ }
+
+ const auto isActive = [](const QHttp2Stream *stream) { return stream && stream->isActive(); };
+ if (std::none_of(m_streams.cbegin(), m_streams.cend(), isActive))
+ closeSession();
+}
+
+void QHttp2Connection::handleWINDOW_UPDATE()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
+
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ const auto streamID = inboundFrame.streamID();
+
+ qCDebug(qHttp2ConnectionLog(), "[%p] Received WINDOW_UPDATE, stream %d, delta %d", this,
+ streamID, delta);
+ if (streamID == connectionStreamID) {
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
+ return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
+ sessionSendWindowSize = sum;
+ for (auto &stream : m_streams) {
+ if (!stream || !stream->isActive())
+ continue;
+ // Stream may have been unblocked, so maybe try to write again
+ if (stream->isUploadingDATA() && !stream->isUploadBlocked())
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ } else {
+ QHttp2Stream *stream = m_streams.value(streamID);
+ if (!stream || !stream->isActive()) {
+ // WINDOW_UPDATE on closed streams can be ignored.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received WINDOW_UPDATE on closed stream %d", this,
+ streamID);
+ return;
+ }
+ stream->handleWINDOW_UPDATE(inboundFrame);
+ }
+}
+
+void QHttp2Connection::handleCONTINUATION()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
+ if (continuedFrames.empty())
+ return connectionError(PROTOCOL_ERROR,
+ "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
+
+ if (inboundFrame.streamID() != continuedFrames.front().streamID())
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION on invalid stream");
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders)
+ return;
+
+ continuationExpected = false;
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handleContinuedHEADERS()
+{
+ // 'Continued' HEADERS can be: the initial HEADERS/PUSH_PROMISE frame
+ // with/without END_HEADERS flag set plus, if no END_HEADERS flag,
+ // a sequence of one or more CONTINUATION frames.
+ Q_ASSERT(!continuedFrames.empty());
+ const auto firstFrameType = continuedFrames[0].type();
+ Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
+
+ const auto streamID = continuedFrames[0].streamID();
+
+ const auto streamIt = m_streams.constFind(streamID);
+ if (firstFrameType == FrameType::HEADERS) {
+ if (streamIt != m_streams.cend()) {
+ QHttp2Stream *stream = streamIt.value();
+ if (stream->state() != QHttp2Stream::State::HalfClosedLocal
+ && stream->state() != QHttp2Stream::State::ReservedRemote
+ && stream->state() != QHttp2Stream::State::Idle
+ && stream->state() != QHttp2Stream::State::Open) {
+ // We can receive HEADERS on streams initiated by our requests
+ // (these streams are in halfClosedLocal or open state) or
+ // remote-reserved streams from a server's PUSH_PROMISE.
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "HEADERS on invalid stream"_L1);
+ stream->sendRST_STREAM(CANCEL);
+ return;
+ }
+ }
+ // Else: we cannot just ignore our peer's HEADERS frames - they change
+ // HPACK context - even though the stream was reset; apparently the peer
+ // has yet to see the reset.
+ }
+
+ std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else {
+ if (firstFrameType == FrameType::PUSH_PROMISE) {
+ // It could be a PRIORITY sent in HEADERS - already handled by this
+ // point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
+ // "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
+ // frames MUST be a valid and complete set of request header fields
+ // (Section 8.1.2.3) ... If a client receives a PUSH_PROMISE that does
+ // not include a complete and valid set of header fields or the :method
+ // pseudo-header field identifies a method that is not safe, it MUST
+ // respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
+ if (streamIt != m_streams.cend())
+ (*streamIt)->sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+
+ // We got back an empty hpack block. Now let's figure out if there was an error.
+ constexpr auto hpackBlockHasContent = [](const auto &c) { return c.hpackBlockSize() > 0; };
+ const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
+ hpackBlockHasContent);
+ if (anyHpackBlock) // There was hpack block data, but returned empty => it overflowed.
+ return connectionError(FRAME_SIZE_ERROR, "HEADERS frame too large");
+ }
+
+ if (streamIt == m_streams.cend()) // No more processing without a stream from here on.
+ return;
+
+ switch (firstFrameType) {
+ case FrameType::HEADERS:
+ streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ break;
+ case FrameType::PUSH_PROMISE: {
+ std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
+ if (!promiseKey)
+ return; // invalid URL/key !
+ if (m_promisedStreams.contains(*promiseKey))
+ return; // already promised!
+ const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
+ QHttp2Stream *stream = m_streams.value(promiseID);
+ stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
+ stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ emit newPromisedStream(stream); // @future[consider] add promise key as argument?
+ m_promisedStreams.emplace(*promiseKey, promiseID);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
+{
+ switch (identifier) {
+ case Settings::HEADER_TABLE_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS HEADER_TABLE_SIZE %d", this, newValue);
+ if (newValue > maxAcceptableTableSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS invalid table size");
+ return false;
+ }
+ encoder.setMaxDynamicTableSize(newValue);
+ break;
+ }
+ case Settings::INITIAL_WINDOW_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d", this,
+ newValue);
+ // For every active stream - adjust its window
+ // (and handle possible overflows as errors).
+ if (newValue > quint32(std::numeric_limits<qint32>::max())) {
+ connectionError(FLOW_CONTROL_ERROR, "SETTINGS invalid initial window size");
+ return false;
+ }
+
+ const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
+ streamInitialSendWindowSize = qint32(newValue);
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Adjusting initial window size for %zu streams by %d",
+ this, size_t(m_streams.size()), delta);
+ for (const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
+ if (!stream || !stream->isActive())
+ continue;
+ qint32 sum = 0;
+ if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
+ stream->sendRST_STREAM(PROTOCOL_ERROR);
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "SETTINGS window overflow"_L1);
+ continue;
+ }
+ stream->m_sendWindow = sum;
+ if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ }
+ break;
+ }
+ case Settings::MAX_CONCURRENT_STREAMS_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d", this,
+ newValue);
+ m_maxConcurrentStreams = newValue;
+ break;
+ }
+ case Settings::MAX_FRAME_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_FRAME_SIZE %d", this, newValue);
+ if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS max frame size is out of range");
+ return false;
+ }
+ maxFrameSize = newValue;
+ break;
+ }
+ case Settings::MAX_HEADER_LIST_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d", this,
+ newValue);
+ // We just remember this value, it can later
+ // prevent us from sending any request (and this
+ // will end up in request/reply error).
+ m_maxHeaderListSize = newValue;
+ break;
+ }
+ case Http2::Settings::ENABLE_PUSH_ID:
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ENABLE_PUSH %d", this, newValue);
+ if (newValue != 0 && newValue != 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS peer sent illegal value for ENABLE_PUSH");
+ return false;
+ }
+ if (m_connectionType == Type::Client) {
+ if (newValue == 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS server sent ENABLE_PUSH=1");
+ return false;
+ }
+ } else { // server-side
+ pushPromiseEnabled = newValue;
+ break;
+ }
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp2connection_p.cpp"
diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h
new file mode 100644
index 0000000000..ca2cae58e0
--- /dev/null
+++ b/src/network/access/qhttp2connection_p.h
@@ -0,0 +1,372 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef HTTP2CONNECTION_P_H
+#define HTTP2CONNECTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qxpfunctional.h>
+#include <QtNetwork/qhttp2configuration.h>
+#include <QtNetwork/qtcpsocket.h>
+
+#include <private/http2protocol_p.h>
+#include <private/http2streams_p.h>
+#include <private/http2frames_p.h>
+#include <private/hpack_p.h>
+
+#include <variant>
+#include <optional>
+#include <type_traits>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T, typename Err>
+class QH2Expected
+{
+ static_assert(!std::is_same_v<T, Err>, "T and Err must be different types");
+public:
+ // Rule Of Zero applies
+ QH2Expected(T &&value) : m_data(std::move(value)) { }
+ QH2Expected(const T &value) : m_data(value) { }
+ QH2Expected(Err &&error) : m_data(std::move(error)) { }
+ QH2Expected(const Err &error) : m_data(error) { }
+
+ QH2Expected &operator=(T &&value)
+ {
+ m_data = std::move(value);
+ return *this;
+ }
+ QH2Expected &operator=(const T &value)
+ {
+ m_data = value;
+ return *this;
+ }
+ QH2Expected &operator=(Err &&error)
+ {
+ m_data = std::move(error);
+ return *this;
+ }
+ QH2Expected &operator=(const Err &error)
+ {
+ m_data = error;
+ return *this;
+ }
+ T unwrap() const
+ {
+ Q_ASSERT(ok());
+ return std::get<T>(m_data);
+ }
+ Err error() const
+ {
+ Q_ASSERT(has_error());
+ return std::get<Err>(m_data);
+ }
+ bool ok() const noexcept { return std::holds_alternative<T>(m_data); }
+ bool has_value() const noexcept { return ok(); }
+ bool has_error() const noexcept { return std::holds_alternative<Err>(m_data); }
+ void clear() noexcept { m_data.reset(); }
+
+private:
+ std::variant<T, Err> m_data;
+};
+
+class QHttp2Connection;
+class Q_NETWORK_EXPORT QHttp2Stream : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Stream)
+
+public:
+ enum class State { Idle, ReservedRemote, Open, HalfClosedLocal, HalfClosedRemote, Closed };
+ Q_ENUM(State)
+ constexpr static quint8 DefaultPriority = 127;
+
+ ~QHttp2Stream() noexcept;
+
+ // HTTP2 things
+ quint32 streamID() const noexcept { return m_streamID; }
+
+ // Are we waiting for a larger send window before sending more data?
+ bool isUploadBlocked() const noexcept;
+ bool isUploadingDATA() const noexcept { return m_uploadByteDevice != nullptr; }
+ State state() const noexcept { return m_state; }
+ bool isActive() const noexcept { return m_state != State::Closed && m_state != State::Idle; }
+ bool isPromisedStream() const noexcept { return m_isReserved; }
+ bool wasReset() const noexcept { return m_RST_STREAM_code.has_value(); }
+ quint32 RST_STREAM_code() const noexcept { return m_RST_STREAM_code.value_or(0); }
+ // Just the list of headers, as received, may contain duplicates:
+ HPack::HttpHeader receivedHeaders() const noexcept { return m_headers; }
+
+ QByteDataBuffer downloadBuffer() const noexcept { return m_downloadBuffer; }
+
+Q_SIGNALS:
+ void headersReceived(const HPack::HttpHeader &headers, bool endStream);
+ void headersUpdated();
+ void errorOccurred(quint32 errorCode, const QString &errorString);
+ void stateChanged(QHttp2Stream::State newState);
+ void promisedStreamReceived(quint32 newStreamID);
+ void uploadBlocked();
+ void dataReceived(const QByteArray &data, bool endStream);
+
+ void bytesWritten(qint64 bytesWritten);
+ void uploadDeviceError(const QString &errorString);
+ void uploadFinished();
+
+public Q_SLOTS:
+ bool sendRST_STREAM(quint32 errorCode);
+ bool sendHEADERS(const HPack::HttpHeader &headers, bool endStream,
+ quint8 priority = DefaultPriority);
+ void sendDATA(QIODevice *device, bool endStream);
+ void sendDATA(QNonContiguousByteDevice *device, bool endStream);
+ void sendWINDOW_UPDATE(quint32 delta);
+
+private Q_SLOTS:
+ void maybeResumeUpload();
+ void uploadDeviceReadChannelFinished();
+ void uploadDeviceDestroyed();
+
+private:
+ friend class QHttp2Connection;
+ QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept;
+
+ [[nodiscard]] QHttp2Connection *getConnection() const
+ {
+ return qobject_cast<QHttp2Connection *>(parent());
+ }
+
+ enum class StateTransition {
+ Open,
+ CloseLocal,
+ CloseRemote,
+ RST,
+ };
+
+ void setState(State newState);
+ void transitionState(StateTransition transition);
+ void internalSendDATA();
+ void finishSendDATA();
+
+ void handleDATA(const Http2::Frame &inboundFrame);
+ void handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers);
+ void handleRST_STREAM(const Http2::Frame &inboundFrame);
+ void handleWINDOW_UPDATE(const Http2::Frame &inboundFrame);
+
+ void finishWithError(quint32 errorCode, const QString &message);
+ void finishWithError(quint32 errorCode);
+
+ // Keep it const since it never changes after creation
+ const quint32 m_streamID = 0;
+ qint32 m_recvWindow = 0;
+ qint32 m_sendWindow = 0;
+ bool m_endStreamAfterDATA = false;
+ std::optional<quint32> m_RST_STREAM_code;
+
+ QIODevice *m_uploadDevice = nullptr;
+ QNonContiguousByteDevice *m_uploadByteDevice = nullptr;
+
+ QByteDataBuffer m_downloadBuffer;
+ State m_state = State::Idle;
+ HPack::HttpHeader m_headers;
+ bool m_isReserved = false;
+};
+
+class Q_NETWORK_EXPORT QHttp2Connection : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Connection)
+
+public:
+ enum class CreateStreamError {
+ MaxConcurrentStreamsReached,
+ StreamIdsExhausted,
+ ReceivedGOAWAY,
+ };
+ Q_ENUM(CreateStreamError)
+
+ enum class PingState {
+ Ping,
+ PongSignatureIdentical,
+ PongSignatureChanged,
+ PongNoPingSent, // We got an ACKed ping but had not sent any
+ };
+
+ // For a pre-established connection:
+ [[nodiscard]] static QHttp2Connection *
+ createUpgradedConnection(QIODevice *socket, const QHttp2Configuration &config);
+ // For a new connection, potential TLS handshake must already be finished:
+ [[nodiscard]] static QHttp2Connection *createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config);
+ [[nodiscard]] static QHttp2Connection *
+ createDirectServerConnection(QIODevice *socket, const QHttp2Configuration &config);
+ ~QHttp2Connection();
+
+ [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream();
+
+ QHttp2Stream *getStream(quint32 streamId) const;
+ QHttp2Stream *promisedStream(const QUrl &streamKey) const
+ {
+ if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
+ return m_streams.value(id);
+ return nullptr;
+ }
+
+ void close() { sendGOAWAY(Http2::HTTP2_NO_ERROR); }
+
+ bool isGoingAway() const noexcept { return m_goingAway; }
+
+ quint32 maxConcurrentStreams() const noexcept { return m_maxConcurrentStreams; }
+ quint32 maxHeaderListSize() const noexcept { return m_maxHeaderListSize; }
+
+ bool isUpgradedConnection() const noexcept { return m_upgradedConnection; }
+
+Q_SIGNALS:
+ void newIncomingStream(QHttp2Stream *stream);
+ void newPromisedStream(QHttp2Stream *stream);
+ void errorReceived(/*@future: add as needed?*/); // Connection errors only, no stream-specific errors
+ void connectionClosed();
+ void settingsFrameReceived();
+ void pingFrameRecived(QHttp2Connection::PingState state);
+ void errorOccurred(Http2::Http2Error errorCode, const QString &errorString);
+ void receivedGOAWAY(quint32 errorCode, quint32 lastStreamID);
+public Q_SLOTS:
+ bool sendPing();
+ bool sendPing(QByteArrayView data);
+ void handleReadyRead();
+ void handleConnectionClosure();
+
+private:
+ friend class QHttp2Stream;
+ [[nodiscard]] QIODevice *getSocket() const { return qobject_cast<QIODevice *>(parent()); }
+
+ QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createStreamInternal();
+ QHttp2Stream *createStreamInternal_impl(quint32 streamID);
+
+ bool isInvalidStream(quint32 streamID) noexcept;
+ bool streamWasReset(quint32 streamID) noexcept;
+
+ void connectionError(Http2::Http2Error errorCode,
+ const char *message); // Connection failed to be established?
+ void setH2Configuration(QHttp2Configuration config);
+ void closeSession();
+ qsizetype numActiveStreamsImpl(quint32 mask) const noexcept;
+ qsizetype numActiveRemoteStreams() const noexcept;
+ qsizetype numActiveLocalStreams() const noexcept;
+
+ bool sendClientPreface();
+ bool sendSETTINGS();
+ bool sendServerPreface();
+ bool serverCheckClientPreface();
+ bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
+ bool sendGOAWAY(quint32 errorCode);
+ bool sendSETTINGS_ACK();
+
+ void handleDATA();
+ void handleHEADERS();
+ void handlePRIORITY();
+ void handleRST_STREAM();
+ void handleSETTINGS();
+ void handlePUSH_PROMISE();
+ void handlePING();
+ void handleGOAWAY();
+ void handleWINDOW_UPDATE();
+ void handleCONTINUATION();
+
+ void handleContinuedHEADERS();
+
+ bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+
+ bool readClientPreface();
+
+ explicit QHttp2Connection(QIODevice *socket);
+
+ enum class Type { Client, Server } m_connectionType = Type::Client;
+
+ bool waitingForSettingsACK = false;
+
+ static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ // HTTP/2 4.3: Header compression is stateful. One compression context and
+ // one decompression context are used for the entire connection.
+ HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
+ HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize, true);
+
+ QHttp2Configuration m_config;
+ QHash<quint32, QPointer<QHttp2Stream>> m_streams;
+ QHash<QUrl, quint32> m_promisedStreams;
+ QVarLengthArray<quint32> m_resetStreamIDs;
+ std::optional<QByteArray> m_lastPingSignature = std::nullopt;
+ quint32 m_nextStreamID = 1;
+
+ // Peer's max frame size (this min is the default value
+ // we start with, that can be updated by SETTINGS frame):
+ quint32 maxFrameSize = Http2::minPayloadLimit;
+
+ Http2::FrameReader frameReader;
+ Http2::Frame inboundFrame;
+ Http2::FrameWriter frameWriter;
+
+ // Temporary storage to assemble HEADERS' block
+ // from several CONTINUATION frames ...
+ bool continuationExpected = false;
+ std::vector<Http2::Frame> continuedFrames;
+
+ // Control flow:
+
+ // This is how many concurrent streams our peer allows us, 100 is the
+ // initial value, can be updated by the server's SETTINGS frame(s):
+ quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
+ // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer,
+ // it's just a hint and we do not actually enforce it (and we can continue
+ // sending requests and creating streams while maxConcurrentStreams allows).
+
+ // This is our (client-side) maximum possible receive window size, we set
+ // it in a ctor from QHttp2Configuration, it does not change after that.
+ // The default is 64Kb:
+ qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our session current receive window size, updated in a ctor from
+ // QHttp2Configuration. Signed integer since it can become negative
+ // (it's still a valid window size).
+ qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+ // Our per-stream receive window size, default is 64 Kb, will be updated
+ // from QHttp2Configuration. Again, signed - can become negative.
+ qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // These are our peer's receive window sizes, they will be updated by the
+ // peer's SETTINGS and WINDOW_UPDATE frames, defaults presumed to be 64Kb.
+ qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
+ qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our peer's header size limitations. It's unlimited by default, but can
+ // be changed via peer's SETTINGS frame.
+ quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
+ // While we can send SETTINGS_MAX_HEADER_LIST_SIZE value (our limit on
+ // the headers size), we never enforce it, it's just a hint to our peer.
+
+ bool m_upgradedConnection = false;
+ bool m_goingAway = false;
+ bool pushPromiseEnabled = false;
+ quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
+
+ // Server-side only:
+ bool m_waitingForClientPreface = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // HTTP2CONNECTION_P_H
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index dce51d4fd5..d9341dc643 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkconnection_p.h"
#include "qhttp2protocolhandler_p.h"
@@ -46,10 +10,12 @@
#include <private/qnoncontiguousbytedevice_p.h>
#include <QtNetwork/qabstractsocket.h>
+
#include <QtCore/qloggingcategory.h>
#include <QtCore/qendian.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/qurl.h>
#include <qhttp2configuration.h>
@@ -62,9 +28,12 @@
#include <algorithm>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace
{
@@ -79,10 +48,10 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
// 1. Before anything - mandatory fields, if they do not fit into maxHeaderList -
// then stop immediately with error.
const auto auth = request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1();
- header.push_back(HeaderField(":authority", auth));
- header.push_back(HeaderField(":method", request.methodName()));
- header.push_back(HeaderField(":path", request.uri(useProxy)));
- header.push_back(HeaderField(":scheme", request.url().scheme().toLatin1()));
+ header.emplace_back(":authority", auth);
+ header.emplace_back(":method", request.methodName());
+ header.emplace_back(":path", request.uri(useProxy));
+ header.emplace_back(":scheme", request.url().scheme().toLatin1());
HeaderSize size = header_size(header);
if (!size.first) // Ooops!
@@ -91,9 +60,11 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
return HttpHeader(); // Bad, we cannot send this request ...
- const auto requestHeader = request.header();
- for (const auto &field : requestHeader) {
- const HeaderSize delta = entry_size(field.first, field.second);
+ const QHttpHeaders requestHeader = request.header();
+ for (qsizetype i = 0; i < requestHeader.size(); ++i) {
+ const auto name = requestHeader.nameAt(i);
+ const auto value = requestHeader.valueAt(i);
+ const HeaderSize delta = entry_size(name, value);
if (!delta.first) // Overflow???
break;
if (std::numeric_limits<quint32>::max() - delta.second < size.second)
@@ -102,73 +73,38 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
break;
- if (field.first.compare("connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("host", Qt::CaseInsensitive) == 0 ||
- field.first.compare("keep-alive", Qt::CaseInsensitive) == 0 ||
- field.first.compare("proxy-connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("transfer-encoding", Qt::CaseInsensitive) == 0)
+ if (name == "connection"_L1 || name == "host"_L1 || name == "keep-alive"_L1
+ || name == "proxy-connection"_L1 || name == "transfer-encoding"_L1) {
continue; // Those headers are not valid (section 3.2.1) - from QSpdyProtocolHandler
+ }
// TODO: verify with specs, which fields are valid to send ....
- // toLower - 8.1.2 .... "header field names MUST be converted to lowercase prior
- // to their encoding in HTTP/2.
- // A request or response containing uppercase header field names
- // MUST be treated as malformed (Section 8.1.2.6)".
- header.push_back(HeaderField(field.first.toLower(), field.second));
+ //
+ // Note: RFC 7450 8.1.2 (HTTP/2) states that header field names must be lower-cased
+ // prior to their encoding in HTTP/2; header name fields in QHttpHeaders are already
+ // lower-cased
+ header.emplace_back(QByteArray{name.data(), name.size()},
+ QByteArray{value.data(), value.size()});
}
return header;
}
-std::vector<uchar> assemble_hpack_block(const std::vector<Http2::Frame> &frames)
-{
- std::vector<uchar> hpackBlock;
-
- quint32 total = 0;
- for (const auto &frame : frames)
- total += frame.hpackBlockSize();
-
- if (!total)
- return hpackBlock;
-
- hpackBlock.resize(total);
- auto dst = hpackBlock.begin();
- for (const auto &frame : frames) {
- if (const auto hpackBlockSize = frame.hpackBlockSize()) {
- const uchar *src = frame.hpackBlockBegin();
- std::copy(src, src + hpackBlockSize, dst);
- dst += hpackBlockSize;
- }
- }
-
- return hpackBlock;
-}
-
QUrl urlkey_from_request(const QHttpNetworkRequest &request)
{
QUrl url;
url.setScheme(request.url().scheme());
url.setAuthority(request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo));
- url.setPath(QLatin1String(request.uri(false)));
+ url.setPath(QLatin1StringView(request.uri(false)));
return url;
}
-bool sum_will_overflow(qint32 windowSize, qint32 delta)
-{
- if (windowSize > 0)
- return std::numeric_limits<qint32>::max() - windowSize < delta;
- return std::numeric_limits<qint32>::min() - windowSize > delta;
-}
-
}// Unnamed namespace
// Since we anyway end up having this in every function definition:
using namespace Http2;
-const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
-const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
-
QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel),
decoder(HPack::FieldLookupTable::DefaultSize),
@@ -220,6 +156,12 @@ void QHttp2ProtocolHandler::handleConnectionClosure()
goingAway = true;
}
+void QHttp2ProtocolHandler::ensureClientPrefaceSent()
+{
+ if (!prefaceSent)
+ sendClientPreface();
+}
+
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
{
if (!sender()) // QueuedConnection, firing after sender (byte device) was deleted.
@@ -233,8 +175,7 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
auto &stream = activeStreams[streamID];
if (!sendDATA(stream)) {
- finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA"));
+ finishStreamWithError(stream, QNetworkReply::UnknownNetworkError, "failed to send DATA"_L1);
sendRST_STREAM(streamID, INTERNAL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -258,7 +199,8 @@ void QHttp2ProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData)
void QHttp2ProtocolHandler::_q_readyRead()
{
- _q_receiveReply();
+ if (!goingAway || activeStreams.size())
+ _q_receiveReply();
}
void QHttp2ProtocolHandler::_q_receiveReply()
@@ -266,6 +208,11 @@ void QHttp2ProtocolHandler::_q_receiveReply()
Q_ASSERT(m_socket);
Q_ASSERT(m_channel);
+ if (goingAway && activeStreams.isEmpty()) {
+ m_channel->close();
+ return;
+ }
+
while (!goingAway || activeStreams.size()) {
const auto result = frameReader.read(*m_socket);
switch (result) {
@@ -332,18 +279,16 @@ bool QHttp2ProtocolHandler::sendRequest()
// so we cannot create new streams.
m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
"GOAWAY received, cannot start a request");
- m_channel->spdyRequestsToSend.clear();
+ m_channel->h2RequestsToSend.clear();
return false;
}
// Process 'fake' (created by QNetworkAccessManager::connectToHostEncrypted())
// requests first:
- auto &requests = m_channel->spdyRequestsToSend;
+ auto &requests = m_channel->h2RequestsToSend;
for (auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
const auto &pair = *it;
- const QString scheme(pair.first.url().scheme());
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https")) {
+ if (pair.first.isPreConnect()) {
m_connection->preConnectFinished();
emit pair.second->finished();
it = requests.erase(it);
@@ -381,10 +326,13 @@ bool QHttp2ProtocolHandler::sendRequest()
initReplyFromPushPromise(message, key);
}
- const auto streamsToUse = std::min<quint32>(maxConcurrentStreams - activeStreams.size(),
- requests.size());
+ const auto isClientSide = [](const auto &pair) -> bool { return (pair.first & 1) == 1; };
+ const auto activeClientSideStreams = std::count_if(
+ activeStreams.constKeyValueBegin(), activeStreams.constKeyValueEnd(), isClientSide);
+ const qint64 streamsToUse = qBound(0, qint64(maxConcurrentStreams) - activeClientSideStreams,
+ requests.size());
auto it = requests.begin();
- for (quint32 i = 0; i < streamsToUse; ++i) {
+ for (qint64 i = 0; i < streamsToUse; ++i) {
const qint32 newStreamID = createNewStream(*it);
if (!newStreamID) {
// TODO: actually we have to open a new connection.
@@ -397,14 +345,14 @@ bool QHttp2ProtocolHandler::sendRequest()
Stream &newStream = activeStreams[newStreamID];
if (!sendHEADERS(newStream)) {
finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send HEADERS frame(s)"));
+ "failed to send HEADERS frame(s)"_L1);
deleteActiveStream(newStreamID);
continue;
}
if (newStream.data() && !sendDATA(newStream)) {
finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA frame(s)"));
+ "failed to send DATA frame(s)"_L1);
sendRST_STREAM(newStreamID, INTERNAL_ERROR);
markAsReset(newStreamID);
deleteActiveStream(newStreamID);
@@ -483,6 +431,10 @@ bool QHttp2ProtocolHandler::sendHEADERS(Stream &stream)
#ifndef QT_NO_NETWORKPROXY
useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
#endif
+ if (stream.request().withCredentials()) {
+ m_connection->d_func()->createAuthorization(m_socket, stream.request());
+ stream.request().d->needResendWithCredentials = false;
+ }
const auto headers = build_headers(stream.request(), maxHeaderListSize, useProxy);
if (!headers.size()) // nothing fits into maxHeaderListSize
return false;
@@ -508,7 +460,7 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
Q_ASSERT(replyPrivate);
auto slot = std::min<qint32>(sessionSendWindowSize, stream.sendWindow);
- while (!stream.data()->atEnd() && slot) {
+ while (replyPrivate->totallyUploadedData < request.contentLength() && slot) {
qint64 chunkSize = 0;
const uchar *src =
reinterpret_cast<const uchar *>(stream.data()->readPointer(slot, chunkSize));
@@ -523,7 +475,7 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
}
frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
- const qint32 bytesWritten = std::min<qint32>(slot, chunkSize);
+ const qint32 bytesWritten = qint32(std::min<qint64>(slot, chunkSize));
if (!frameWriter.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
return false;
@@ -595,12 +547,12 @@ void QHttp2ProtocolHandler::handleDATA()
sessionReceiveWindowSize -= inboundFrame.payloadSize();
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ auto it = activeStreams.find(streamID);
+ if (it != activeStreams.end()) {
+ Stream &stream = it.value();
if (qint32(inboundFrame.payloadSize()) > stream.recvWindow) {
- finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("flow control error"));
+ finishStreamWithError(stream, QNetworkReply::ProtocolFailure, "flow control error"_L1);
sendRST_STREAM(streamID, FLOW_CONTROL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -673,7 +625,8 @@ void QHttp2ProtocolHandler::handlePRIORITY()
quint32 streamDependency = 0;
uchar weight = 0;
const bool noErr = inboundFrame.priority(&streamDependency, &weight);
- Q_UNUSED(noErr) Q_ASSERT(noErr);
+ Q_UNUSED(noErr);
+ Q_ASSERT(noErr);
const bool exclusive = streamDependency & 0x80000000;
@@ -862,7 +815,7 @@ void QHttp2ProtocolHandler::handleGOAWAY()
m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
"GOAWAY received, cannot start a request");
// Also, prevent further calls to sendRequest:
- m_channel->spdyRequestsToSend.clear();
+ m_channel->h2RequestsToSend.clear();
QNetworkReply::NetworkError error = QNetworkReply::NoError;
QString message;
@@ -873,7 +826,7 @@ void QHttp2ProtocolHandler::handleGOAWAY()
// successful completion.
if (errorCode == HTTP2_NO_ERROR) {
error = QNetworkReply::ContentReSendError;
- message = QLatin1String("Server stopped accepting new streams before this stream was established");
+ message = "Server stopped accepting new streams before this stream was established"_L1;
}
for (quint32 id = lastStreamID; id < nextID; id += 2) {
@@ -902,24 +855,27 @@ void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
const auto streamID = inboundFrame.streamID();
if (streamID == Http2::connectionStreamID) {
- if (!valid || sum_will_overflow(sessionSendWindowSize, delta))
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
- sessionSendWindowSize += delta;
+ sessionSendWindowSize = sum;
} else {
- if (!activeStreams.contains(streamID)) {
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end()) {
// WINDOW_UPDATE on closed streams can be ignored.
return;
}
- auto &stream = activeStreams[streamID];
- if (!valid || sum_will_overflow(stream.sendWindow, delta)) {
+ Stream &stream = it.value();
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(stream.sendWindow, qint32(delta), &sum)) {
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("invalid WINDOW_UPDATE delta"));
+ "invalid WINDOW_UPDATE delta"_L1);
sendRST_STREAM(streamID, PROTOCOL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
return;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
// Since we're in _q_receiveReply at the moment, let's first handle other
@@ -958,9 +914,10 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
const auto streamID = continuedFrames[0].streamID();
+ const auto streamIt = activeStreams.find(streamID);
if (firstFrameType == FrameType::HEADERS) {
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
if (stream.state != Stream::halfClosedLocal
&& stream.state != Stream::remoteReserved
&& stream.state != Stream::open) {
@@ -968,7 +925,7 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// (these streams are in halfClosedLocal or open state) or
// remote-reserved streams from a server's PUSH_PROMISE.
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("HEADERS on invalid stream"));
+ "HEADERS on invalid stream"_L1);
sendRST_STREAM(streamID, CANCEL);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -982,8 +939,13 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// has yet to see the reset.
}
- std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
- if (!hpackBlock.size()) {
+ std::vector<uchar> hpackBlock(Http2::assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else if (firstFrameType == FrameType::PUSH_PROMISE) {
// It could be a PRIORITY sent in HEADERS - already handled by this
// point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
// "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
@@ -992,23 +954,20 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// not include a complete and valid set of header fields or the :method
// pseudo-header field identifies a method that is not safe, it MUST
// respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
- if (firstFrameType == FrameType::PUSH_PROMISE)
- resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
-
+ resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
return;
}
- HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
- if (!decoder.decodeHeaderFields(inputStream))
- return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
-
switch (firstFrameType) {
case FrameType::HEADERS:
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
- updateStream(stream, decoder.decodedHeader());
- // No DATA frames.
- if (continuedFrames[0].flags() & FrameFlag::END_STREAM) {
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
+ if (hasHeaderFields)
+ updateStream(stream, decoder.decodedHeader());
+ // Needs to resend the request; we should finish and delete the current stream
+ const bool needResend = stream.request().d->needResendWithCredentials;
+ // No DATA frames. Or needs to resend.
+ if (continuedFrames[0].flags() & FrameFlag::END_STREAM || needResend) {
finishStream(stream);
deleteActiveStream(stream.streamID);
}
@@ -1047,17 +1006,18 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
std::vector<quint32> brokenStreams;
brokenStreams.reserve(activeStreams.size());
for (auto &stream : activeStreams) {
- if (sum_will_overflow(stream.sendWindow, delta)) {
+ qint32 sum = 0;
+ if (qAddOverflow(stream.sendWindow, delta, &sum)) {
brokenStreams.push_back(stream.streamID);
continue;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
for (auto id : brokenStreams) {
auto &stream = activeStreams[id];
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("SETTINGS window overflow"));
+ "SETTINGS window overflow"_L1);
sendRST_STREAM(id, PROTOCOL_ERROR);
markAsReset(id);
deleteActiveStream(id);
@@ -1066,17 +1026,12 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
QMetaObject::invokeMethod(this, "resumeSuspendedStreams", Qt::QueuedConnection);
}
- if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID) {
- if (newValue > maxPeerConcurrentStreams) {
- connectionError(PROTOCOL_ERROR, "SETTINGS invalid number of concurrent streams");
- return false;
- }
+ if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID)
maxConcurrentStreams = newValue;
- }
if (identifier == Settings::MAX_FRAME_SIZE_ID) {
if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
- connectionError(PROTOCOL_ERROR, "SETTGINGS max frame size is out of range");
+ connectionError(PROTOCOL_ERROR, "SETTINGS max frame size is out of range");
return false;
}
maxFrameSize = newValue;
@@ -1096,7 +1051,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
Qt::ConnectionType connectionType)
{
const auto httpReply = stream.reply();
- const auto &httpRequest = stream.request();
+ auto &httpRequest = stream.request();
Q_ASSERT(httpReply || stream.state == Stream::remoteReserved);
if (!httpReply) {
@@ -1121,11 +1076,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
// moment and we are probably not done yet. So we extract url and set it
// here, if needed.
int statusCode = 0;
- QUrl redirectUrl;
-
for (const auto &pair : headers) {
const auto &name = pair.name;
- auto value = pair.value;
+ const auto value = QByteArrayView(pair.value);
// TODO: part of this code copies what SPDY protocol handler does when
// processing headers. Binary nature of HTTP/2 and SPDY saves us a lot
@@ -1134,40 +1087,57 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (name == ":status") {
statusCode = value.left(3).toInt();
httpReply->setStatusCode(statusCode);
- httpReplyPrivate->reasonPhrase = QString::fromLatin1(value.mid(4));
+ m_channel->lastStatus = statusCode; // Mostly useless for http/2, needed for auth
+ httpReply->setReasonPhrase(QString::fromLatin1(value.mid(4)));
} else if (name == ":version") {
- httpReplyPrivate->majorVersion = value.at(5) - '0';
- httpReplyPrivate->minorVersion = value.at(7) - '0';
+ httpReply->setMajorVersion(value.at(5) - '0');
+ httpReply->setMinorVersion(value.at(7) - '0');
} else if (name == "content-length") {
bool ok = false;
const qlonglong length = value.toLongLong(&ok);
if (ok)
httpReply->setContentLength(length);
} else {
- if (name == "location")
- redirectUrl = QUrl::fromEncoded(value);
- QByteArray binder(", ");
- if (name == "set-cookie")
- binder = "\n";
- httpReplyPrivate->fields.append(qMakePair(name, value.replace('\0', binder)));
+ const auto binder = name == "set-cookie" ? QByteArrayView("\n") : QByteArrayView(", ");
+ httpReply->appendHeaderField(name, QByteArray(pair.value).replace('\0', binder));
}
}
- if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid())
- httpReply->setRedirectUrl(redirectUrl);
+ // Discard all informational (1xx) replies with the exception of 101.
+ // Also see RFC 9110 (Chapter 15.2)
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
+ httpReplyPrivate->clearHttpLayerInformation();
+ return;
+ }
+
+ if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
+ QHttpNetworkConnectionPrivate::ParseRedirectResult result =
+ m_connection->d_func()->parseRedirectResponse(httpReply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ auto errorString = m_connection->d_func()->errorDetail(result.errorCode, m_socket);
+ finishStreamWithError(stream, result.errorCode, errorString);
+ sendRST_STREAM(stream.streamID, INTERNAL_ERROR);
+ markAsReset(stream.streamID);
+ return;
+ }
+
+ if (result.redirectUrl.isValid())
+ httpReply->setRedirectUrl(result.redirectUrl);
+ }
if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
httpReplyPrivate->removeAutoDecompressHeader();
- if (QHttpNetworkReply::isHttpRedirect(statusCode)
- || statusCode == 401 || statusCode == 407) {
- // These are the status codes that can trigger uploadByteDevice->reset()
- // in QHttpNetworkConnectionChannel::handleStatus. Alas, we have no
- // single request/reply, we multiplex several requests and thus we never
- // simply call 'handleStatus'. If we have byte-device - we try to reset
- // it here, we don't (and can't) handle any error during reset operation.
- if (stream.data())
+ if (QHttpNetworkReply::isHttpRedirect(statusCode)) {
+ // Note: This status code can trigger uploadByteDevice->reset() in
+ // QHttpNetworkConnectionChannel::handleStatus. Alas, we have no single
+ // request/reply, we multiplex several requests and thus we never simply
+ // call 'handleStatus'. If we have a byte-device - we try to reset it
+ // here, we don't (and can't) handle any error during reset operation.
+ if (stream.data()) {
stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
}
if (connectionType == Qt::DirectConnection)
@@ -1193,19 +1163,11 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
if (const auto length = frame.dataSize()) {
const char *data = reinterpret_cast<const char *>(frame.dataBegin());
- auto &httpRequest = stream.request();
auto replyPrivate = httpReply->d_func();
replyPrivate->totalProgress += length;
- const QByteArray wrapped(data, length);
- if (httpRequest.d->autoDecompress && replyPrivate->isCompressed()) {
- QByteDataBuffer inDataBuffer;
- inDataBuffer.append(wrapped);
- replyPrivate->uncompressBodyData(&inDataBuffer, &replyPrivate->responseData);
- } else {
- replyPrivate->responseData.append(wrapped);
- }
+ replyPrivate->responseData.append(QByteArray(data, length));
if (replyPrivate->shouldEmitSignals()) {
if (connectionType == Qt::DirectConnection) {
@@ -1222,6 +1184,91 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
}
}
+// After calling this function, either the request will be re-sent or
+// the reply will be finishedWithError! Do not emit finished() or similar on the
+// reply after this!
+void QHttp2ProtocolHandler::handleAuthorization(Stream &stream)
+{
+ auto *httpReply = stream.reply();
+ auto *httpReplyPrivate = httpReply->d_func();
+ auto &httpRequest = stream.request();
+
+ Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
+
+ const auto handleAuth = [&, this](QByteArrayView authField, bool isProxy) -> bool {
+ Q_ASSERT(httpReply);
+ const QByteArrayView auth = authField.trimmed();
+ if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
+ // @todo: We're supposed to fall back to http/1.1:
+ // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
+ // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
+ // In this case IIS will fall back to HTTP/1.1."
+ // Though it might be OK to ignore this. The server shouldn't let us connect with
+ // HTTP/2 if it doesn't support us using it.
+ return false;
+ }
+ // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
+ bool resend = false;
+ const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
+ m_socket, httpReply, isProxy, resend);
+ if (authenticateHandled) {
+ if (resend) {
+ httpReply->d_func()->eraseData();
+ // Add the request back in queue, we'll retry later now that
+ // we've gotten some username/password set on it:
+ httpRequest.d->needResendWithCredentials = true;
+ m_channel->h2RequestsToSend.insert(httpRequest.priority(), stream.httpPair);
+ httpReply->d_func()->clearHeaders();
+ // If we have data we were uploading we need to reset it:
+ if (stream.data()) {
+ stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
+ // We automatically try to send new requests when the stream is
+ // closed, so we don't need to call sendRequest ourselves.
+ return true;
+ } // else: we're just not resending the request.
+ // @note In the http/1.x case we (at time of writing) call close()
+ // for the connectionChannel (which is a bit weird, we could surely
+ // reuse the open socket outside "connection:close"?), but in http2
+ // we only have one channel, so we won't close anything.
+ } else {
+ // No authentication header or authentication isn't supported, but
+ // we got a 401/407 so we cannot succeed. We need to emit signals
+ // for headers and data, and then finishWithError.
+ emit httpReply->headerChanged();
+ emit httpReply->readyRead();
+ QNetworkReply::NetworkError error = httpReply->statusCode() == 401
+ ? QNetworkReply::AuthenticationRequiredError
+ : QNetworkReply::ProxyAuthenticationRequiredError;
+ finishStreamWithError(stream, QNetworkReply::AuthenticationRequiredError,
+ m_connection->d_func()->errorDetail(error, m_socket));
+ }
+ return false;
+ };
+
+ // These statuses would in HTTP/1.1 be handled by
+ // QHttpNetworkConnectionChannel::handleStatus. But because h2 has
+ // multiple streams/requests in a single channel this structure does not
+ // map properly to that function.
+ bool authOk = true;
+ switch (httpReply->statusCode()) {
+ case 401:
+ authOk = handleAuth(httpReply->headerField("www-authenticate"), false);
+ break;
+ case 407:
+ authOk = handleAuth(httpReply->headerField("proxy-authenticate"), true);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ if (authOk) {
+ markAsReset(stream.streamID);
+ deleteActiveStream(stream.streamID);
+ } // else: errors handled inside handleAuth
+}
+
+// Called when we have received a frame with the END_STREAM flag set
void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType connectionType)
{
Q_ASSERT(stream.state == Stream::remoteReserved || stream.reply());
@@ -1229,14 +1276,25 @@ void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType conn
stream.state = Stream::closed;
auto httpReply = stream.reply();
if (httpReply) {
+ int statusCode = httpReply->statusCode();
+ if (statusCode == 401 || statusCode == 407) {
+ // handleAuthorization will either re-send the request or
+ // finishWithError. In either case we don't want to emit finished
+ // here.
+ handleAuthorization(stream);
+ return;
+ }
+
httpReply->disconnect(this);
if (stream.data())
stream.data()->disconnect(this);
- if (connectionType == Qt::DirectConnection)
- emit httpReply->finished();
- else
- QMetaObject::invokeMethod(httpReply, "finished", connectionType);
+ if (!stream.request().d->needResendWithCredentials) {
+ if (connectionType == Qt::DirectConnection)
+ emit httpReply->finished();
+ else
+ QMetaObject::invokeMethod(httpReply, "finished", connectionType);
+ }
}
qCDebug(QT_HTTP2) << "stream" << stream.streamID << "closed";
@@ -1281,7 +1339,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b
const auto replyPrivate = reply->d_func();
replyPrivate->connection = m_connection;
replyPrivate->connectionChannel = m_channel;
- reply->setSpdyWasUsed(true);
+ reply->setHttp2WasUsed(true);
streamIDs.insert(reply, newStreamID);
connect(reply, SIGNAL(destroyed(QObject*)),
this, SLOT(_q_replyDestroyed(QObject*)));
@@ -1300,6 +1358,8 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b
}
}
+ QMetaObject::invokeMethod(reply, "requestSent", Qt::QueuedConnection);
+
activeStreams.insert(newStreamID, newStream);
return newStreamID;
@@ -1348,9 +1408,10 @@ quint32 QHttp2ProtocolHandler::popStreamToResume()
auto &queue = suspendedStreams[rank];
auto it = queue.begin();
for (; it != queue.end(); ++it) {
- if (!activeStreams.contains(*it))
+ auto stream = activeStreams.constFind(*it);
+ if (stream == activeStreams.cend())
continue;
- if (activeStreams[*it].sendWindow > 0)
+ if (stream->sendWindow > 0)
break;
}
@@ -1373,8 +1434,8 @@ void QHttp2ProtocolHandler::removeFromSuspended(quint32 streamID)
void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
{
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ if (const auto it = activeStreams.constFind(streamID); it != activeStreams.cend()) {
+ const Stream &stream = it.value();
if (stream.reply()) {
stream.reply()->disconnect(this);
streamIDs.remove(stream.reply());
@@ -1383,11 +1444,11 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
stream.data()->disconnect(this);
streamIDs.remove(stream.data());
}
- activeStreams.remove(streamID);
+ activeStreams.erase(it);
}
removeFromSuspended(streamID);
- if (m_channel->spdyRequestsToSend.size())
+ if (m_channel->h2RequestsToSend.size())
QMetaObject::invokeMethod(this, "sendRequest", Qt::QueuedConnection);
}
@@ -1406,13 +1467,14 @@ void QHttp2ProtocolHandler::resumeSuspendedStreams()
if (!streamID)
return;
- if (!activeStreams.contains(streamID))
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end())
continue;
+ Stream &stream = it.value();
- Stream &stream = activeStreams[streamID];
if (!sendDATA(stream)) {
finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA"));
+ "failed to send DATA"_L1);
sendRST_STREAM(streamID, INTERNAL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -1438,42 +1500,18 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram
{
Q_ASSERT(pushPromiseFrame.type() == FrameType::PUSH_PROMISE);
- QMap<QByteArray, QByteArray> pseudoHeaders;
- for (const auto &field : requestHeader) {
- if (field.name == ":scheme" || field.name == ":path"
- || field.name == ":authority" || field.name == ":method") {
- if (field.value.isEmpty() || pseudoHeaders.contains(field.name))
- return false;
- pseudoHeaders[field.name] = field.value;
- }
- }
-
- if (pseudoHeaders.size() != 4) {
- // All four required, HTTP/2 8.1.2.3.
- return false;
- }
-
- const QByteArray method = pseudoHeaders[":method"];
- if (method.compare("get", Qt::CaseInsensitive) != 0 &&
- method.compare("head", Qt::CaseInsensitive) != 0)
- return false;
-
- QUrl url;
- url.setScheme(QLatin1String(pseudoHeaders[":scheme"]));
- url.setAuthority(QLatin1String(pseudoHeaders[":authority"]));
- url.setPath(QLatin1String(pseudoHeaders[":path"]));
-
- if (!url.isValid())
+ const auto url = HPack::makePromiseKeyUrl(requestHeader);
+ if (!url.has_value())
return false;
Q_ASSERT(activeStreams.contains(pushPromiseFrame.streamID()));
const Stream &associatedStream = activeStreams[pushPromiseFrame.streamID()];
const auto associatedUrl = urlkey_from_request(associatedStream.request());
- if (url.adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
+ if (url->adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
return false;
- const auto urlKey = url.toString();
+ const auto urlKey = url->toString();
if (promisedData.contains(urlKey)) // duplicate push promise
return false;
@@ -1506,14 +1544,14 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
Q_ASSERT(promisedData.contains(cacheKey));
auto promise = promisedData.take(cacheKey);
Q_ASSERT(message.second);
- message.second->setSpdyWasUsed(true);
+ message.second->setHttp2WasUsed(true);
qCDebug(QT_HTTP2) << "found cached/promised response on stream" << promise.reservedID;
bool replyFinished = false;
Stream *promisedStream = nullptr;
- if (activeStreams.contains(promise.reservedID)) {
- promisedStream = &activeStreams[promise.reservedID];
+ if (auto it = activeStreams.find(promise.reservedID); it != activeStreams.end()) {
+ promisedStream = &it.value();
// Ok, we have an active (not closed yet) stream waiting for more frames,
// let's pretend we requested it:
promisedStream->httpPair = message;
@@ -1523,8 +1561,8 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
streamInitialSendWindowSize,
streamInitialReceiveWindowSize);
closedStream.state = Stream::halfClosedLocal;
- activeStreams.insert(promise.reservedID, closedStream);
- promisedStream = &activeStreams[promise.reservedID];
+ it = activeStreams.insert(promise.reservedID, closedStream);
+ promisedStream = &it.value();
replyFinished = true;
}
@@ -1558,7 +1596,7 @@ void QHttp2ProtocolHandler::connectionError(Http2::Http2Error errorCode,
m_channel->emitFinishedWithError(error, message);
for (auto &stream: activeStreams)
- finishStreamWithError(stream, error, QLatin1String(message));
+ finishStreamWithError(stream, error, QLatin1StringView(message));
closeSession();
}
@@ -1574,3 +1612,5 @@ void QHttp2ProtocolHandler::closeSession()
}
QT_END_NAMESPACE
+
+#include "moc_qhttp2protocolhandler_p.cpp"
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index 43fdb136cd..3b818771a6 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTP2PROTOCOLHANDLER_P_H
#define QHTTP2PROTOCOLHANDLER_P_H
@@ -93,6 +57,7 @@ public:
QHttp2ProtocolHandler &operator = (QHttp2ProtocolHandler &&rhs) = delete;
Q_INVOKABLE void handleConnectionClosure();
+ Q_INVOKABLE void ensureClientPrefaceSent();
private slots:
void _q_uploadDataReadyRead();
@@ -129,6 +94,7 @@ private:
bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+ void handleAuthorization(Stream &stream);
void updateStream(Stream &stream, const HPack::HttpHeader &headers,
Qt::ConnectionType connectionType = Qt::DirectConnection);
void updateStream(Stream &stream, const Http2::Frame &dataFrame,
@@ -155,7 +121,7 @@ private:
// the client's preface 24-byte message.
bool waitingForSettingsACK = false;
- static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ inline static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
// HTTP/2 4.3: Header compression is stateful. One compression context and
// one decompression context are used for the entire connection.
HPack::Decoder decoder;
@@ -164,7 +130,7 @@ private:
QHash<QObject *, int> streamIDs;
QHash<quint32, Stream> activeStreams;
std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low.
- static const std::deque<quint32>::size_type maxRecycledStreams;
+ inline static const std::deque<quint32>::size_type maxRecycledStreams = 10000;
std::deque<quint32> recycledStreams;
// Peer's max frame size (this min is the default value
diff --git a/src/network/access/qhttpheaderparser.cpp b/src/network/access/qhttpheaderparser.cpp
new file mode 100644
index 0000000000..0b7882c18a
--- /dev/null
+++ b/src/network/access/qhttpheaderparser.cpp
@@ -0,0 +1,221 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttpheaderparser_p.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+QHttpHeaderParser::QHttpHeaderParser()
+ : statusCode(100) // Required by tst_QHttpNetworkConnection::ignoresslerror(failure)
+ , majorVersion(0)
+ , minorVersion(0)
+{
+}
+
+void QHttpHeaderParser::clear()
+{
+ statusCode = 100;
+ majorVersion = 0;
+ minorVersion = 0;
+ reasonPhrase.clear();
+ fields.clear();
+}
+
+static bool fieldNameCheck(QByteArrayView name)
+{
+ static constexpr QByteArrayView otherCharacters("!#$%&'*+-.^_`|~");
+ static const auto fieldNameChar = [](char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
+ || otherCharacters.contains(c);
+ };
+
+ return !name.empty() && std::all_of(name.begin(), name.end(), fieldNameChar);
+}
+
+bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
+{
+ // see rfc2616, sec 4 for information about HTTP/1.1 headers.
+ // allows relaxed parsing here, accepts both CRLF & LF line endings
+ Q_ASSERT(fields.isEmpty());
+ const auto hSpaceStart = [](QByteArrayView h) {
+ return h.startsWith(' ') || h.startsWith('\t');
+ };
+ // Headers, if non-empty, start with a non-space and end with a newline:
+ if (hSpaceStart(header) || (!header.empty() && !header.endsWith('\n')))
+ return false;
+
+ while (int tail = header.endsWith("\n\r\n") ? 2 : header.endsWith("\n\n") ? 1 : 0)
+ header.chop(tail);
+
+ if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
+ return false;
+
+ QHttpHeaders result;
+ while (!header.empty()) {
+ const qsizetype colon = header.indexOf(':');
+ if (colon == -1) // if no colon check if empty headers
+ return result.isEmpty() && (header == "\n" || header == "\r\n");
+ if (result.size() >= maxFieldCount)
+ return false;
+ QByteArrayView name = header.first(colon);
+ if (!fieldNameCheck(name))
+ return false;
+ header = header.sliced(colon + 1);
+ QByteArray value;
+ qsizetype valueSpace = maxFieldSize - name.size() - 1;
+ do {
+ const qsizetype endLine = header.indexOf('\n');
+ Q_ASSERT(endLine != -1);
+ auto line = header.first(endLine); // includes space
+ valueSpace -= line.size() - (line.endsWith('\r') ? 1 : 0);
+ if (valueSpace < 0)
+ return false;
+ line = line.trimmed();
+ if (!line.empty()) {
+ if (value.size())
+ value += ' ' + line;
+ else
+ value = line.toByteArray();
+ }
+ header = header.sliced(endLine + 1);
+ } while (hSpaceStart(header));
+ Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
+ result.append(name, value);
+ }
+
+ fields = result;
+ return true;
+}
+
+bool QHttpHeaderParser::parseStatus(QByteArrayView status)
+{
+ // from RFC 2616:
+ // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+ // that makes: 'HTTP/n.n xxx Message'
+ // byte count: 0123456789012
+
+ static const int minLength = 11;
+ static const int dotPos = 6;
+ static const int spacePos = 8;
+ static const char httpMagic[] = "HTTP/";
+
+ if (status.size() < minLength
+ || !status.startsWith(httpMagic)
+ || status.at(dotPos) != '.'
+ || status.at(spacePos) != ' ') {
+ // I don't know how to parse this status line
+ return false;
+ }
+
+ // optimize for the valid case: defer checking until the end
+ majorVersion = status.at(dotPos - 1) - '0';
+ minorVersion = status.at(dotPos + 1) - '0';
+
+ int i = spacePos;
+ qsizetype j = status.indexOf(' ', i + 1);
+ const QByteArrayView code = j > i ? status.sliced(i + 1, j - i - 1)
+ : status.sliced(i + 1);
+
+ bool ok = false;
+ statusCode = code.toInt(&ok);
+
+ reasonPhrase = j > i ? QString::fromLatin1(status.sliced(j + 1))
+ : QString();
+
+ return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
+}
+
+const QHttpHeaders& QHttpHeaderParser::headers() const
+{
+ return fields;
+}
+
+QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
+ const QByteArray &defaultValue) const
+{
+ return fields.value(name, defaultValue).toByteArray();
+}
+
+QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const
+{
+ const QList<QByteArray> allValues = headerFieldValues(name);
+ if (allValues.isEmpty())
+ return defaultValue;
+ return allValues.join(", ");
+}
+
+QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const
+{
+ return fields.values(name);
+}
+
+void QHttpHeaderParser::removeHeaderField(QByteArrayView name)
+{
+ fields.removeAll(name);
+}
+
+void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ removeHeaderField(name);
+ fields.append(name, data);
+}
+
+void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ fields.insert(0, name, data);
+}
+
+void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ fields.append(name, data);
+}
+
+void QHttpHeaderParser::clearHeaders()
+{
+ fields.clear();
+}
+
+int QHttpHeaderParser::getStatusCode() const
+{
+ return statusCode;
+}
+
+void QHttpHeaderParser::setStatusCode(int code)
+{
+ statusCode = code;
+}
+
+int QHttpHeaderParser::getMajorVersion() const
+{
+ return majorVersion;
+}
+
+void QHttpHeaderParser::setMajorVersion(int version)
+{
+ majorVersion = version;
+}
+
+int QHttpHeaderParser::getMinorVersion() const
+{
+ return minorVersion;
+}
+
+void QHttpHeaderParser::setMinorVersion(int version)
+{
+ minorVersion = version;
+}
+
+QString QHttpHeaderParser::getReasonPhrase() const
+{
+ return reasonPhrase;
+}
+
+void QHttpHeaderParser::setReasonPhrase(const QString &reason)
+{
+ reasonPhrase = reason;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheaderparser_p.h b/src/network/access/qhttpheaderparser_p.h
new file mode 100644
index 0000000000..5e8f3c8130
--- /dev/null
+++ b/src/network/access/qhttpheaderparser_p.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTPHEADERPARSER_H
+#define QHTTPHEADERPARSER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QByteArray>
+#include <QList>
+#include <QPair>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+namespace HeaderConstants {
+
+// We previously used 8K, which is common on server side, but it turned out to
+// not be enough for various uses. Historically Firefox used 10K as the limit of
+// a single field, but some Location headers and Authorization challenges can
+// get even longer. Other browsers, such as Chrome, instead have a limit on the
+// total size of all the headers (as well as extra limits on some of the
+// individual fields). We'll use 100K as our default limit, which would be a ridiculously large
+// header, with the possibility to override it where we need to.
+static constexpr int MAX_HEADER_FIELD_SIZE = 100 * 1024;
+// Taken from http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfields
+static constexpr int MAX_HEADER_FIELDS = 100;
+// Chromium has a limit on the total size of the header set to 256KB,
+// which is a reasonable default for QNetworkAccessManager.
+// https://stackoverflow.com/a/3436155
+static constexpr int MAX_TOTAL_HEADER_SIZE = 256 * 1024;
+
+}
+
+class Q_NETWORK_EXPORT QHttpHeaderParser
+{
+public:
+ QHttpHeaderParser();
+
+ void clear();
+ bool parseHeaders(QByteArrayView headers);
+ bool parseStatus(QByteArrayView status);
+
+ const QHttpHeaders& headers() const;
+ void setStatusCode(int code);
+ int getStatusCode() const;
+ int getMajorVersion() const;
+ void setMajorVersion(int version);
+ int getMinorVersion() const;
+ void setMinorVersion(int version);
+ QString getReasonPhrase() const;
+ void setReasonPhrase(const QString &reason);
+
+ QByteArray firstHeaderField(QByteArrayView name,
+ const QByteArray &defaultValue = QByteArray()) const;
+ QByteArray combinedHeaderValue(QByteArrayView name,
+ const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+ void prependHeaderField(const QByteArray &name, const QByteArray &data);
+ void appendHeaderField(const QByteArray &name, const QByteArray &data);
+ void removeHeaderField(QByteArrayView name);
+ void clearHeaders();
+
+ void setMaxHeaderFieldSize(qsizetype size) { maxFieldSize = size; }
+ qsizetype maxHeaderFieldSize() const { return maxFieldSize; }
+
+ void setMaxTotalHeaderSize(qsizetype size) { maxTotalSize = size; }
+ qsizetype maxTotalHeaderSize() const { return maxTotalSize; }
+
+ void setMaxHeaderFields(qsizetype count) { maxFieldCount = count; }
+ qsizetype maxHeaderFields() const { return maxFieldCount; }
+
+private:
+ QHttpHeaders fields;
+ QString reasonPhrase;
+ int statusCode;
+ int majorVersion;
+ int minorVersion;
+
+ qsizetype maxFieldSize = HeaderConstants::MAX_HEADER_FIELD_SIZE;
+ qsizetype maxTotalSize = HeaderConstants::MAX_TOTAL_HEADER_SIZE;
+ qsizetype maxFieldCount = HeaderConstants::MAX_HEADER_FIELDS;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERPARSER_H
diff --git a/src/network/access/qhttpheaders.cpp b/src/network/access/qhttpheaders.cpp
new file mode 100644
index 0000000000..c63da899a8
--- /dev/null
+++ b/src/network/access/qhttpheaders.cpp
@@ -0,0 +1,1551 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttpheaders.h"
+
+#include <private/qoffsetstringarray_p.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtCore/qttypetraits.h>
+
+#include <q20algorithm.h>
+#include <string_view>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQHttpHeaders, "qt.network.http.headers");
+
+/*!
+ \class QHttpHeaders
+ \since 6.7
+ \ingroup
+ \inmodule QtNetwork
+
+ \brief QHttpHeaders is a class for holding HTTP headers.
+
+ The class is an interface type for Qt networking APIs that
+ use or consume such headers.
+
+ \section1 Allowed field name and value characters
+
+ An HTTP header consists of \e name and \e value.
+ When setting these, QHttpHeaders validates \e name and \e value
+ to only contain characters allowed by the HTTP RFCs. For detailed
+ information see
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-values}
+ {RFC 9110 Chapters 5.1 and 5.5}.
+
+ In all, this means:
+ \list
+ \li \c name must consist of visible ASCII characters, and must not be
+ empty
+ \li \c value may consist of arbitrary bytes, as long as header
+ and use case specific encoding rules are adhered to. \c value
+ may be empty
+ \endlist
+
+ The setters of this class automatically remove any leading or trailing
+ whitespaces from \e value, as they must be ignored during the
+ \e value processing.
+
+ \section1 Combining values
+
+ Most HTTP header values can be combined with a single comma \c {','}
+ plus an optional whitespace, and the semantic meaning is preserved.
+ As an example, these two should be semantically similar:
+ \badcode
+ // Values as separate header entries
+ myheadername: myheadervalue1
+ myheadername: myheadervalue2
+ // Combined value
+ myheadername: myheadervalue1, myheadervalue2
+ \endcode
+
+ However, there is a notable exception to this rule:
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}
+ {Set-Cookie}. Due to this and the possibility of custom use cases,
+ QHttpHeaders does not automatically combine the values.
+
+ \section1 Performance
+
+ Most QHttpHeaders functions provide both
+ \l QHttpHeaders::WellKnownHeader and \l QAnyStringView overloads.
+ From a memory-usage and computation point of view it is recommended
+ to use the \l QHttpHeaders::WellKnownHeader overloads.
+*/
+
+// This list is from IANA HTTP Field Name Registry
+// https://www.iana.org/assignments/http-fields
+// It contains entries that are either "permanent"
+// or "deprecated" as of October 2023.
+// Usage relies on enum values keeping in same order.
+// ### Qt7 check if some of these headers have been obsoleted,
+// and also check if the enums benefit from reordering
+static constexpr auto headerNames = qOffsetStringArray(
+ // IANA Permanent status:
+ "a-im",
+ "accept",
+ "accept-additions",
+ "accept-ch",
+ "accept-datetime",
+ "accept-encoding",
+ "accept-features",
+ "accept-language",
+ "accept-patch",
+ "accept-post",
+ "accept-ranges",
+ "accept-signature",
+ "access-control-allow-credentials",
+ "access-control-allow-headers",
+ "access-control-allow-methods",
+ "access-control-allow-origin",
+ "access-control-expose-headers",
+ "access-control-max-age",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "age",
+ "allow",
+ "alpn",
+ "alt-svc",
+ "alt-used",
+ "alternates",
+ "apply-to-redirect-ref",
+ "authentication-control",
+ "authentication-info",
+ "authorization",
+ "cache-control",
+ "cache-status",
+ "cal-managed-id",
+ "caldav-timezones",
+ "capsule-protocol",
+ "cdn-cache-control",
+ "cdn-loop",
+ "cert-not-after",
+ "cert-not-before",
+ "clear-site-data",
+ "client-cert",
+ "client-cert-chain",
+ "close",
+ "connection",
+ "content-digest",
+ "content-disposition",
+ "content-encoding",
+ "content-id",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-security-policy",
+ "content-security-policy-report-only",
+ "content-type",
+ "cookie",
+ "cross-origin-embedder-policy",
+ "cross-origin-embedder-policy-report-only",
+ "cross-origin-opener-policy",
+ "cross-origin-opener-policy-report-only",
+ "cross-origin-resource-policy",
+ "dasl",
+ "date",
+ "dav",
+ "delta-base",
+ "depth",
+ "destination",
+ "differential-id",
+ "dpop",
+ "dpop-nonce",
+ "early-data",
+ "etag",
+ "expect",
+ "expect-ct",
+ "expires",
+ "forwarded",
+ "from",
+ "hobareg",
+ "host",
+ "if",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-range",
+ "if-schedule-tag-match",
+ "if-unmodified-since",
+ "im",
+ "include-referred-token-binding-id",
+ "keep-alive",
+ "label",
+ "last-event-id",
+ "last-modified",
+ "link",
+ "location",
+ "lock-token",
+ "max-forwards",
+ "memento-datetime",
+ "meter",
+ "mime-version",
+ "negotiate",
+ "nel",
+ "odata-entityid",
+ "odata-isolation",
+ "odata-maxversion",
+ "odata-version",
+ "optional-www-authenticate",
+ "ordering-type",
+ "origin",
+ "origin-agent-cluster",
+ "oscore",
+ "oslc-core-version",
+ "overwrite",
+ "ping-from",
+ "ping-to",
+ "position",
+ "prefer",
+ "preference-applied",
+ "priority",
+ "proxy-authenticate",
+ "proxy-authentication-info",
+ "proxy-authorization",
+ "proxy-status",
+ "public-key-pins",
+ "public-key-pins-report-only",
+ "range",
+ "redirect-ref",
+ "referer",
+ "refresh",
+ "replay-nonce",
+ "repr-digest",
+ "retry-after",
+ "schedule-reply",
+ "schedule-tag",
+ "sec-purpose",
+ "sec-token-binding",
+ "sec-websocket-accept",
+ "sec-websocket-extensions",
+ "sec-websocket-key",
+ "sec-websocket-protocol",
+ "sec-websocket-version",
+ "server",
+ "server-timing",
+ "set-cookie",
+ "signature",
+ "signature-input",
+ "slug",
+ "soapaction",
+ "status-uri",
+ "strict-transport-security",
+ "sunset",
+ "surrogate-capability",
+ "surrogate-control",
+ "tcn",
+ "te",
+ "timeout",
+ "topic",
+ "traceparent",
+ "tracestate",
+ "trailer",
+ "transfer-encoding",
+ "ttl",
+ "upgrade",
+ "urgency",
+ "user-agent",
+ "variant-vary",
+ "vary",
+ "via",
+ "want-content-digest",
+ "want-repr-digest",
+ "www-authenticate",
+ "x-content-type-options",
+ "x-frame-options",
+ // IANA Deprecated status:
+ "accept-charset",
+ "c-pep-info",
+ "pragma",
+ "protocol-info",
+ "protocol-query"
+ // If you append here, regenerate the index table
+);
+
+namespace {
+struct ByIndirectHeaderName
+{
+ constexpr bool operator()(quint8 lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(map(lhs), map(rhs));
+ }
+ constexpr bool operator()(quint8 lhs, QByteArrayView rhs) const noexcept
+ {
+ return (*this)(map(lhs), rhs);
+ }
+ constexpr bool operator()(QByteArrayView lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(lhs, map(rhs));
+ }
+ constexpr bool operator()(QByteArrayView lhs, QByteArrayView rhs) const noexcept
+ {
+ // ### just `lhs < rhs` when QByteArrayView relational operators are constexpr
+ return std::string_view(lhs) < std::string_view(rhs);
+ }
+private:
+ static constexpr QByteArrayView map(quint8 i) noexcept
+ {
+ return headerNames.viewAt(i);
+ }
+};
+} // unnamed namespace
+
+// This index table contains the indexes of 'headerNames' entries (above) in alphabetical order.
+// This allows a more efficient binary search for the names [O(logN)]. The 'headerNames' itself
+// cannot be guaranteed to be in alphabetical order, as it must keep the same order as the
+// WellKnownHeader enum, which may get appended over time.
+//
+// Note: when appending new enums, this must be regenerated
+static constexpr quint8 orderedHeaderNameIndexes[] = {
+ 0, // a-im
+ 1, // accept
+ 2, // accept-additions
+ 3, // accept-ch
+ 172, // accept-charset
+ 4, // accept-datetime
+ 5, // accept-encoding
+ 6, // accept-features
+ 7, // accept-language
+ 8, // accept-patch
+ 9, // accept-post
+ 10, // accept-ranges
+ 11, // accept-signature
+ 12, // access-control-allow-credentials
+ 13, // access-control-allow-headers
+ 14, // access-control-allow-methods
+ 15, // access-control-allow-origin
+ 16, // access-control-expose-headers
+ 17, // access-control-max-age
+ 18, // access-control-request-headers
+ 19, // access-control-request-method
+ 20, // age
+ 21, // allow
+ 22, // alpn
+ 23, // alt-svc
+ 24, // alt-used
+ 25, // alternates
+ 26, // apply-to-redirect-ref
+ 27, // authentication-control
+ 28, // authentication-info
+ 29, // authorization
+ 173, // c-pep-info
+ 30, // cache-control
+ 31, // cache-status
+ 32, // cal-managed-id
+ 33, // caldav-timezones
+ 34, // capsule-protocol
+ 35, // cdn-cache-control
+ 36, // cdn-loop
+ 37, // cert-not-after
+ 38, // cert-not-before
+ 39, // clear-site-data
+ 40, // client-cert
+ 41, // client-cert-chain
+ 42, // close
+ 43, // connection
+ 44, // content-digest
+ 45, // content-disposition
+ 46, // content-encoding
+ 47, // content-id
+ 48, // content-language
+ 49, // content-length
+ 50, // content-location
+ 51, // content-range
+ 52, // content-security-policy
+ 53, // content-security-policy-report-only
+ 54, // content-type
+ 55, // cookie
+ 56, // cross-origin-embedder-policy
+ 57, // cross-origin-embedder-policy-report-only
+ 58, // cross-origin-opener-policy
+ 59, // cross-origin-opener-policy-report-only
+ 60, // cross-origin-resource-policy
+ 61, // dasl
+ 62, // date
+ 63, // dav
+ 64, // delta-base
+ 65, // depth
+ 66, // destination
+ 67, // differential-id
+ 68, // dpop
+ 69, // dpop-nonce
+ 70, // early-data
+ 71, // etag
+ 72, // expect
+ 73, // expect-ct
+ 74, // expires
+ 75, // forwarded
+ 76, // from
+ 77, // hobareg
+ 78, // host
+ 79, // if
+ 80, // if-match
+ 81, // if-modified-since
+ 82, // if-none-match
+ 83, // if-range
+ 84, // if-schedule-tag-match
+ 85, // if-unmodified-since
+ 86, // im
+ 87, // include-referred-token-binding-id
+ 88, // keep-alive
+ 89, // label
+ 90, // last-event-id
+ 91, // last-modified
+ 92, // link
+ 93, // location
+ 94, // lock-token
+ 95, // max-forwards
+ 96, // memento-datetime
+ 97, // meter
+ 98, // mime-version
+ 99, // negotiate
+ 100, // nel
+ 101, // odata-entityid
+ 102, // odata-isolation
+ 103, // odata-maxversion
+ 104, // odata-version
+ 105, // optional-www-authenticate
+ 106, // ordering-type
+ 107, // origin
+ 108, // origin-agent-cluster
+ 109, // oscore
+ 110, // oslc-core-version
+ 111, // overwrite
+ 112, // ping-from
+ 113, // ping-to
+ 114, // position
+ 174, // pragma
+ 115, // prefer
+ 116, // preference-applied
+ 117, // priority
+ 175, // protocol-info
+ 176, // protocol-query
+ 118, // proxy-authenticate
+ 119, // proxy-authentication-info
+ 120, // proxy-authorization
+ 121, // proxy-status
+ 122, // public-key-pins
+ 123, // public-key-pins-report-only
+ 124, // range
+ 125, // redirect-ref
+ 126, // referer
+ 127, // refresh
+ 128, // replay-nonce
+ 129, // repr-digest
+ 130, // retry-after
+ 131, // schedule-reply
+ 132, // schedule-tag
+ 133, // sec-purpose
+ 134, // sec-token-binding
+ 135, // sec-websocket-accept
+ 136, // sec-websocket-extensions
+ 137, // sec-websocket-key
+ 138, // sec-websocket-protocol
+ 139, // sec-websocket-version
+ 140, // server
+ 141, // server-timing
+ 142, // set-cookie
+ 143, // signature
+ 144, // signature-input
+ 145, // slug
+ 146, // soapaction
+ 147, // status-uri
+ 148, // strict-transport-security
+ 149, // sunset
+ 150, // surrogate-capability
+ 151, // surrogate-control
+ 152, // tcn
+ 153, // te
+ 154, // timeout
+ 155, // topic
+ 156, // traceparent
+ 157, // tracestate
+ 158, // trailer
+ 159, // transfer-encoding
+ 160, // ttl
+ 161, // upgrade
+ 162, // urgency
+ 163, // user-agent
+ 164, // variant-vary
+ 165, // vary
+ 166, // via
+ 167, // want-content-digest
+ 168, // want-repr-digest
+ 169, // www-authenticate
+ 170, // x-content-type-options
+ 171, // x-frame-options
+};
+static_assert(std::size(orderedHeaderNameIndexes) == size_t(headerNames.count()));
+static_assert(q20::is_sorted(std::begin(orderedHeaderNameIndexes),
+ std::end(orderedHeaderNameIndexes),
+ ByIndirectHeaderName{}));
+
+/*!
+ \enum QHttpHeaders::WellKnownHeader
+
+ List of well known headers as per
+ \l {https://www.iana.org/assignments/http-fields}{IANA registry}.
+
+ \value AIM
+ \value Accept
+ \value AcceptAdditions
+ \value AcceptCH
+ \value AcceptDatetime
+ \value AcceptEncoding
+ \value AcceptFeatures
+ \value AcceptLanguage
+ \value AcceptPatch
+ \value AcceptPost
+ \value AcceptRanges
+ \value AcceptSignature
+ \value AccessControlAllowCredentials
+ \value AccessControlAllowHeaders
+ \value AccessControlAllowMethods
+ \value AccessControlAllowOrigin
+ \value AccessControlExposeHeaders
+ \value AccessControlMaxAge
+ \value AccessControlRequestHeaders
+ \value AccessControlRequestMethod
+ \value Age
+ \value Allow
+ \value ALPN
+ \value AltSvc
+ \value AltUsed
+ \value Alternates
+ \value ApplyToRedirectRef
+ \value AuthenticationControl
+ \value AuthenticationInfo
+ \value Authorization
+ \value CacheControl
+ \value CacheStatus
+ \value CalManagedID
+ \value CalDAVTimezones
+ \value CapsuleProtocol
+ \value CDNCacheControl
+ \value CDNLoop
+ \value CertNotAfter
+ \value CertNotBefore
+ \value ClearSiteData
+ \value ClientCert
+ \value ClientCertChain
+ \value Close
+ \value Connection
+ \value ContentDigest
+ \value ContentDisposition
+ \value ContentEncoding
+ \value ContentID
+ \value ContentLanguage
+ \value ContentLength
+ \value ContentLocation
+ \value ContentRange
+ \value ContentSecurityPolicy
+ \value ContentSecurityPolicyReportOnly
+ \value ContentType
+ \value Cookie
+ \value CrossOriginEmbedderPolicy
+ \value CrossOriginEmbedderPolicyReportOnly
+ \value CrossOriginOpenerPolicy
+ \value CrossOriginOpenerPolicyReportOnly
+ \value CrossOriginResourcePolicy
+ \value DASL
+ \value Date
+ \value DAV
+ \value DeltaBase
+ \value Depth
+ \value Destination
+ \value DifferentialID
+ \value DPoP
+ \value DPoPNonce
+ \value EarlyData
+ \value ETag
+ \value Expect
+ \value ExpectCT
+ \value Expires
+ \value Forwarded
+ \value From
+ \value Hobareg
+ \value Host
+ \value If
+ \value IfMatch
+ \value IfModifiedSince
+ \value IfNoneMatch
+ \value IfRange
+ \value IfScheduleTagMatch
+ \value IfUnmodifiedSince
+ \value IM
+ \value IncludeReferredTokenBindingID
+ \value KeepAlive
+ \value Label
+ \value LastEventID
+ \value LastModified
+ \value Link
+ \value Location
+ \value LockToken
+ \value MaxForwards
+ \value MementoDatetime
+ \value Meter
+ \value MIMEVersion
+ \value Negotiate
+ \value NEL
+ \value ODataEntityId
+ \value ODataIsolation
+ \value ODataMaxVersion
+ \value ODataVersion
+ \value OptionalWWWAuthenticate
+ \value OrderingType
+ \value Origin
+ \value OriginAgentCluster
+ \value OSCORE
+ \value OSLCCoreVersion
+ \value Overwrite
+ \value PingFrom
+ \value PingTo
+ \value Position
+ \value Prefer
+ \value PreferenceApplied
+ \value Priority
+ \value ProxyAuthenticate
+ \value ProxyAuthenticationInfo
+ \value ProxyAuthorization
+ \value ProxyStatus
+ \value PublicKeyPins
+ \value PublicKeyPinsReportOnly
+ \value Range
+ \value RedirectRef
+ \value Referer
+ \value Refresh
+ \value ReplayNonce
+ \value ReprDigest
+ \value RetryAfter
+ \value ScheduleReply
+ \value ScheduleTag
+ \value SecPurpose
+ \value SecTokenBinding
+ \value SecWebSocketAccept
+ \value SecWebSocketExtensions
+ \value SecWebSocketKey
+ \value SecWebSocketProtocol
+ \value SecWebSocketVersion
+ \value Server
+ \value ServerTiming
+ \value SetCookie
+ \value Signature
+ \value SignatureInput
+ \value SLUG
+ \value SoapAction
+ \value StatusURI
+ \value StrictTransportSecurity
+ \value Sunset
+ \value SurrogateCapability
+ \value SurrogateControl
+ \value TCN
+ \value TE
+ \value Timeout
+ \value Topic
+ \value Traceparent
+ \value Tracestate
+ \value Trailer
+ \value TransferEncoding
+ \value TTL
+ \value Upgrade
+ \value Urgency
+ \value UserAgent
+ \value VariantVary
+ \value Vary
+ \value Via
+ \value WantContentDigest
+ \value WantReprDigest
+ \value WWWAuthenticate
+ \value XContentTypeOptions
+ \value XFrameOptions
+ \value AcceptCharset
+ \value CPEPInfo
+ \value Pragma
+ \value ProtocolInfo
+ \value ProtocolQuery
+*/
+
+static QByteArray fieldToByteArray(QLatin1StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QUtf8StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QStringView s)
+{
+ return s.toLatin1();
+}
+
+static QByteArray normalizedName(QAnyStringView name)
+{
+ return name.visit([](auto name){ return fieldToByteArray(name); }).toLower();
+}
+
+struct HeaderName
+{
+ explicit HeaderName(QHttpHeaders::WellKnownHeader name) : data(name)
+ {
+ }
+
+ explicit HeaderName(QAnyStringView name)
+ {
+ auto nname = normalizedName(name);
+ if (auto h = HeaderName::toWellKnownHeader(nname))
+ data = *h;
+ else
+ data = std::move(nname);
+ }
+
+ // Returns an enum corresponding with the 'name' if possible. Uses binary search (O(logN)).
+ // The function doesn't normalize the data; needs to be done by the caller if needed
+ static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QByteArrayView name) noexcept
+ {
+ auto indexesBegin = std::cbegin(orderedHeaderNameIndexes);
+ auto indexesEnd = std::cend(orderedHeaderNameIndexes);
+
+ auto result = std::lower_bound(indexesBegin, indexesEnd, name, ByIndirectHeaderName{});
+
+ if (result != indexesEnd && name == headerNames[*result])
+ return static_cast<QHttpHeaders::WellKnownHeader>(*result);
+ return std::nullopt;
+ }
+
+ QByteArrayView asView() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArrayView {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>)
+ return arg;
+ else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>)
+ return headerNames.viewAt(qToUnderlying(arg));
+ else
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }, data);
+ }
+
+ QByteArray asByteArray() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArray {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>) {
+ return arg;
+ } else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>) {
+ const auto view = headerNames.viewAt(qToUnderlying(arg));
+ return QByteArray::fromRawData(view.constData(), view.size());
+ } else {
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }
+ }, data);
+ }
+
+private:
+ // Store the data as 'enum' whenever possible; more performant, and comparison relies on that
+ std::variant<QHttpHeaders::WellKnownHeader, QByteArray> data;
+
+ friend bool comparesEqual(const HeaderName &lhs, const HeaderName &rhs) noexcept
+ {
+ // Here we compare two std::variants, which will return false if the types don't match.
+ // That is beneficial here because we avoid unnecessary comparisons; but it also means
+ // we must always store the data as WellKnownHeader when possible (in other words, if
+ // we get a string that is mappable to a WellKnownHeader). To guard against accidental
+ // misuse, the 'data' is private and the constructors must be used.
+ return lhs.data == rhs.data;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(HeaderName)
+};
+
+// A clarification on case-sensitivity:
+// - Header *names* are case-insensitive; Content-Type and content-type are considered equal
+// - Header *values* are case-sensitive
+// (In addition, the HTTP/2 and HTTP/3 standards mandate that all headers must be lower-cased when
+// encoded into transmission)
+struct Header {
+ HeaderName name;
+ QByteArray value;
+};
+
+auto headerNameMatches(const HeaderName &name)
+{
+ return [&name](const Header &header) { return header.name == name; };
+}
+
+class QHttpHeadersPrivate : public QSharedData
+{
+public:
+ QHttpHeadersPrivate() = default;
+
+ // The 'Self' is supplied as parameter to static functions so that
+ // we can define common methods which 'detach()' the private itself.
+ using Self = QExplicitlySharedDataPointer<QHttpHeadersPrivate>;
+ static void removeAll(Self &d, const HeaderName &name);
+ static void replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value);
+
+ void combinedValue(const HeaderName &name, QByteArray &result) const;
+ void values(const HeaderName &name, QList<QByteArray> &result) const;
+ QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept;
+
+ QList<Header> headers;
+};
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QHttpHeadersPrivate)
+template <> void QExplicitlySharedDataPointer<QHttpHeadersPrivate>::detach()
+{
+ if (!d) {
+ d = new QHttpHeadersPrivate();
+ d->ref.ref();
+ } else if (d->ref.loadRelaxed() != 1) {
+ detach_helper();
+ }
+}
+
+void QHttpHeadersPrivate::removeAll(Self &d, const HeaderName &name)
+{
+ const auto it = std::find_if(d->headers.cbegin(), d->headers.cend(), headerNameMatches(name));
+
+ if (it != d->headers.cend()) {
+ // Found something to remove, calculate offset so we can proceed from the match-location
+ const auto matchOffset = it - d->headers.cbegin();
+ d.detach();
+ // Rearrange all matches to the end and erase them
+ d->headers.erase(std::remove_if(d->headers.begin() + matchOffset, d->headers.end(),
+ headerNameMatches(name)),
+ d->headers.end());
+ }
+}
+
+void QHttpHeadersPrivate::combinedValue(const HeaderName &name, QByteArray &result) const
+{
+ const char* separator = "";
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name) {
+ result.append(separator);
+ result.append(h.value);
+ separator = ", ";
+ }
+ }
+}
+
+void QHttpHeadersPrivate::values(const HeaderName &name, QList<QByteArray> &result) const
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ result.append(h.value);
+ }
+}
+
+QByteArrayView QHttpHeadersPrivate::value(const HeaderName &name, QByteArrayView defaultValue) const noexcept
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ return h.value;
+ }
+ return defaultValue;
+}
+
+void QHttpHeadersPrivate::replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value)
+{
+ d.detach();
+ auto it = std::find_if(d->headers.begin(), d->headers.end(), headerNameMatches(name));
+ if (it != d->headers.end()) {
+ // Found something to replace => replace, and then rearrange any remaining
+ // matches to the end and erase them
+ it->value = value;
+ d->headers.erase(
+ std::remove_if(it + 1, d->headers.end(), headerNameMatches(name)),
+ d->headers.end());
+ } else {
+ // Found nothing to replace => append
+ d->headers.append(Header{name, value});
+ }
+}
+
+/*!
+ Creates a new QHttpHeaders object.
+*/
+QHttpHeaders::QHttpHeaders() noexcept : d()
+{
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &header : headers)
+ h.append(header.first, header.second);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Disposes of the headers object.
+*/
+QHttpHeaders::~QHttpHeaders()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpHeaders::QHttpHeaders(const QHttpHeaders &other)
+ = default;
+
+/*!
+ Assigns the contents of \a other and returns a reference to this object.
+*/
+QHttpHeaders &QHttpHeaders::operator=(const QHttpHeaders &other)
+ = default;
+
+/*!
+ \fn QHttpHeaders::QHttpHeaders(QHttpHeaders &&other) noexcept
+
+ Move-constructs the object from \a other, which will be left
+ \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn QHttpHeaders &QHttpHeaders::operator=(QHttpHeaders &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this object.
+
+ \a other will be left \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn void QHttpHeaders::swap(QHttpHeaders &other)
+
+ Swaps this QHttpHeaders with \a other. This function is very fast and
+ never fails.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QHttpHeaders::operator<<(QDebug debug,
+ const QHttpHeaders &headers)
+
+ Writes \a headers into \a debug stream.
+*/
+QDebug operator<<(QDebug debug, const QHttpHeaders &headers)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QHttpHeaders(";
+ if (headers.d) {
+ debug << "headers = ";
+ const char *separator = "";
+ for (const auto &h : headers.d->headers) {
+ debug << separator << h.name.asView() << ':' << h.value;
+ separator = " | ";
+ }
+ }
+ debug << ")";
+ return debug;
+}
+#endif
+
+// A clarification on string encoding:
+// Setters and getters only accept names and values that are Latin-1 representable:
+// Either they are directly ASCII/Latin-1, or if they are UTF-X, they only use first 256
+// of the unicode points. For example using a '€' (U+20AC) in value would yield a warning
+// and the call is ignored.
+// Furthermore the 'name' has more strict rules than the 'value'
+
+// TODO FIXME REMOVEME once this is merged:
+// https://codereview.qt-project.org/c/qt/qtbase/+/508829
+static bool isUtf8Latin1Representable(QUtf8StringView s) noexcept
+{
+ // L1 encoded in UTF8 has at most the form
+ // - 0b0XXX'XXXX - US-ASCII
+ // - 0b1100'00XX 0b10XX'XXXX - at most 8 non-zero LSB bits allowed in L1
+ bool inMultibyte = false;
+ for (unsigned char c : s) {
+ if (c < 128) { // US-ASCII
+ if (inMultibyte)
+ return false; // invalid sequence
+ } else {
+ // decode as UTF-8:
+ if ((c & 0b1110'0000) == 0b1100'0000) { // two-octet UTF-8 leader
+ if (inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = true;
+ const auto bits_7_to_11 = c & 0b0001'1111;
+ if (bits_7_to_11 < 0b10)
+ return false; // invalid sequence (US-ASCII encoded in two octets)
+ if (bits_7_to_11 > 0b11) // more than the two LSB
+ return false; // outside L1
+ } else if ((c & 0b1100'0000) == 0b1000'0000) { // trailing UTF-8 octet
+ if (!inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = false; // only one continuation allowed
+ } else {
+ return false; // invalid sequence or outside of L1
+ }
+ }
+ }
+ if (inMultibyte)
+ return false; // invalid sequence: premature end
+ return true;
+}
+
+static constexpr auto isValidHttpHeaderNameChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapters "5.1 Field Names" and "5.6.2 Tokens"
+ // field-name = token
+ // token = 1*tchar
+ // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /
+ // "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+ // / DIGIT / ALPHA
+ // ; any VCHAR, except delimiters
+ // (for explanation on VCHAR see isValidHttpHeaderValueChar)
+ return (('A' <= c && c <= 'Z')
+ || ('a' <= c && c <= 'z')
+ || ('0' <= c && c <= '9')
+ || ('#' <= c && c <= '\'')
+ || ('^' <= c && c <= '`')
+ || c == '|' || c == '~' || c == '!' || c == '*' || c == '+' || c == '-' || c == '.');
+};
+
+static bool headerNameValidImpl(QLatin1StringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), isValidHttpHeaderNameChar);
+}
+
+static bool headerNameValidImpl(QUtf8StringView name) noexcept
+{
+ // Traversing the UTF-8 string char-by-char is fine in this case as
+ // the isValidHttpHeaderNameChar rejects any value above 0x7E. UTF-8
+ // only has bytes <= 0x7F if they truly represent that ASCII character.
+ return headerNameValidImpl(QLatin1StringView(QByteArrayView(name)));
+}
+
+static bool headerNameValidImpl(QStringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), [](QChar c) {
+ return isValidHttpHeaderNameChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderNameField(QAnyStringView name) noexcept
+{
+ if (name.isEmpty()) {
+ qCWarning(lcQHttpHeaders, "HTTP header name cannot be empty");
+ return false;
+ }
+ const bool valid = name.visit([](auto name){ return headerNameValidImpl(name); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header name contained illegal character(s)");
+ return valid;
+}
+
+static constexpr auto isValidHttpHeaderValueChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapter 5.5, Field Values
+ // field-value = *field-content
+ // field-content = field-vchar
+ // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
+ // field-vchar = VCHAR / obs-text
+ // obs-text = %x80-FF
+ // VCHAR is defined as "any visible US-ASCII character", and RFC 5234 B.1.
+ // defines it as %x21-7E
+ // Note: The ABNF above states that field-content and thus field-value cannot
+ // start or end with SP/HTAB. The caller should handle this.
+ return (c >= 0x80 // obs-text (extended ASCII)
+ || (0x20 <= c && c <= 0x7E) // SP (0x20) + VCHAR
+ || (c == 0x09)); // HTAB
+};
+
+static bool headerValueValidImpl(QLatin1StringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QUtf8StringView value) noexcept
+{
+ if (!isUtf8Latin1Representable(value)) // TODO FIXME see the function
+ return false;
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QStringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), [](QChar c) {
+ return isValidHttpHeaderValueChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderValueField(QAnyStringView value) noexcept
+{
+ const bool valid = value.visit([](auto value){ return headerValueValidImpl(value); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header value contained illegal character(s)");
+ return valid;
+}
+
+static QByteArray normalizedValue(QAnyStringView value)
+{
+ // Note on trimming away any leading or trailing whitespace of 'value':
+ // RFC 9110 (HTTP 1.1, 2022, Chapter 5.5) does not allow leading or trailing whitespace
+ // RFC 7230 (HTTP 1.1, 2014, Chapter 3.2) allows them optionally, but also mandates that
+ // they are ignored during processing
+ // RFC 7540 (HTTP/2) does not seem explicit about it
+ // => for maximum compatibility, trim away any leading or trailing whitespace
+ return value.visit([](auto value){ return fieldToByteArray(value); }).trimmed();
+}
+
+/*!
+ Appends a header entry with \a name and \a value and returns \c true
+ if successful.
+
+ \sa append(QHttpHeaders::WellKnownHeader, QAnyStringView)
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::append(QAnyStringView name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload append(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::append(WellKnownHeader name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Inserts a header entry at index \a i, with \a name and \a value. The index
+ must be valid (see \l size()). Returns whether the insert succeeded.
+
+ \sa append(),
+ insert(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::insert(qsizetype i, QAnyStringView name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload insert(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::insert(qsizetype i, WellKnownHeader name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Replaces the header entry at index \a i, with \a name and \a newValue.
+ The index must be valid (see \l size()). Returns whether the replace
+ succeeded.
+
+ \sa append(),
+ replace(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::replace(qsizetype i, QAnyStringView name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \overload replace(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \since 6.8
+
+ If QHttpHeaders already contains \a name, replaces its value with
+ \a newValue and removes possible additional \a name entries.
+ If \a name didn't exist, appends a new entry. Returns \c true
+ if successful.
+
+ This function is a convenience method for setting a unique
+ \a name : \a newValue header. For most headers the relative order does not
+ matter, which allows reusing an existing entry if one exists.
+
+ \sa replaceOrAppend(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(WellKnownHeader name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ \overload replaceOrAppend(WellKnownHeader, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(QAnyStringView name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ Returns whether the headers contain header with \a name.
+
+ \sa contains(QHttpHeaders::WellKnownHeader)
+*/
+bool QHttpHeaders::contains(QAnyStringView name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ \overload has(QAnyStringView)
+*/
+bool QHttpHeaders::contains(WellKnownHeader name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ Removes the header \a name.
+
+ \sa removeAt(), removeAll(QHttpHeaders::WellKnownHeader)
+*/
+void QHttpHeaders::removeAll(QAnyStringView name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ \overload removeAll(QAnyStringView)
+*/
+void QHttpHeaders::removeAll(WellKnownHeader name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ Removes the header at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa removeAll(QHttpHeaders::WellKnownHeader),
+ removeAll(QAnyStringView), size()
+*/
+void QHttpHeaders::removeAt(qsizetype i)
+{
+ verify(i);
+ d.detach();
+ d->headers.removeAt(i);
+}
+
+/*!
+ Returns the value of the (first) header \a name, or \a defaultValue if it
+ doesn't exist.
+
+ \sa value(QHttpHeaders::WellKnownHeader, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(QAnyStringView name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ \overload value(QAnyStringView, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(WellKnownHeader name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ Returns the values of header \a name in a list. Returns an empty
+ list if header with \a name doesn't exist.
+
+ \sa values(QHttpHeaders::WellKnownHeader)
+*/
+QList<QByteArray> QHttpHeaders::values(QAnyStringView name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload values(QAnyStringView)
+*/
+QList<QByteArray> QHttpHeaders::values(WellKnownHeader name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the header value at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa size(), value(), values(), combinedValue(), nameAt()
+*/
+QByteArrayView QHttpHeaders::valueAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return d->headers.at(i).value;
+}
+
+/*!
+ Returns the header name at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ Header names are case-insensitive, and the returned names are lower-cased.
+
+ \sa size(), valueAt()
+*/
+QLatin1StringView QHttpHeaders::nameAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return QLatin1StringView{d->headers.at(i).name.asView()};
+}
+
+/*!
+ Returns the values of header \a name in a comma-combined string.
+ Returns a \c null QByteArray if the header with \a name doesn't
+ exist.
+
+ \note Accessing the value(s) of 'Set-Cookie' header this way may not work
+ as intended. It is a notable exception in the
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}{HTTP RFC}
+ in that its values cannot be combined this way. Prefer \l values() instead.
+
+ \sa values(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(QAnyStringView name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload combinedValue(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(WellKnownHeader name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the number of header entries.
+*/
+qsizetype QHttpHeaders::size() const noexcept
+{
+ if (!d)
+ return 0;
+ return d->headers.size();
+}
+
+/*!
+ Attempts to allocate memory for at least \a size header entries.
+
+ If you know in advance how how many header entries there will
+ be, you may call this function to prevent reallocations
+ and memory fragmentation.
+*/
+void QHttpHeaders::reserve(qsizetype size)
+{
+ d.detach();
+ d->headers.reserve(size);
+}
+
+/*!
+ \fn bool QHttpHeaders::isEmpty() const noexcept
+
+ Returns \c true if the headers have size 0; otherwise returns \c false.
+
+ \sa size()
+*/
+
+/*!
+ Returns a header name corresponding to the provided \a name as a view.
+*/
+QByteArrayView QHttpHeaders::wellKnownHeaderName(WellKnownHeader name) noexcept
+{
+ return headerNames[qToUnderlying(name)];
+}
+
+/*!
+ Returns the header entries as a list of (name, value) pairs.
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QList<std::pair<QByteArray, QByteArray>> QHttpHeaders::toListOfPairs() const
+{
+ QList<std::pair<QByteArray, QByteArray>> list;
+ if (isEmpty())
+ return list;
+ list.reserve(size());
+ for (const auto & h : std::as_const(d->headers))
+ list.append({h.name.asByteArray(), h.value});
+ return list;
+}
+
+/*!
+ Returns the header entries as a map from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiMap<QByteArray, QByteArray> QHttpHeaders::toMultiMap() const
+{
+ QMultiMap<QByteArray, QByteArray> map;
+ if (isEmpty())
+ return map;
+ for (const auto &h : std::as_const(d->headers))
+ map.insert(h.name.asByteArray(), h.value);
+ return map;
+}
+
+/*!
+ Returns the header entries as a hash from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiHash<QByteArray, QByteArray> QHttpHeaders::toMultiHash() const
+{
+ QMultiHash<QByteArray, QByteArray> hash;
+ if (isEmpty())
+ return hash;
+ hash.reserve(size());
+ for (const auto &h : std::as_const(d->headers))
+ hash.insert(h.name.asByteArray(), h.value);
+ return hash;
+}
+
+/*!
+ Clears all header entries.
+
+ \sa size()
+*/
+void QHttpHeaders::clear()
+{
+ if (isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h
new file mode 100644
index 0000000000..260df1421b
--- /dev/null
+++ b/src/network/access/qhttpheaders.h
@@ -0,0 +1,282 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTPHEADERS_H
+#define QHTTPHEADERS_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qcontainerfwd.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QHttpHeadersPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QHttpHeadersPrivate, Q_NETWORK_EXPORT)
+class QHttpHeaders
+{
+ Q_GADGET_EXPORT(Q_NETWORK_EXPORT)
+public:
+ enum class WellKnownHeader {
+ // IANA Permanent status:
+ AIM,
+ Accept,
+ AcceptAdditions,
+ AcceptCH,
+ AcceptDatetime,
+ AcceptEncoding,
+ AcceptFeatures,
+ AcceptLanguage,
+ AcceptPatch,
+ AcceptPost,
+ AcceptRanges,
+ AcceptSignature,
+ AccessControlAllowCredentials,
+ AccessControlAllowHeaders,
+ AccessControlAllowMethods,
+ AccessControlAllowOrigin,
+ AccessControlExposeHeaders,
+ AccessControlMaxAge,
+ AccessControlRequestHeaders,
+ AccessControlRequestMethod,
+ Age,
+ Allow,
+ ALPN,
+ AltSvc,
+ AltUsed,
+ Alternates,
+ ApplyToRedirectRef,
+ AuthenticationControl,
+ AuthenticationInfo,
+ Authorization,
+ CacheControl,
+ CacheStatus,
+ CalManagedID,
+ CalDAVTimezones,
+ CapsuleProtocol,
+ CDNCacheControl,
+ CDNLoop,
+ CertNotAfter,
+ CertNotBefore,
+ ClearSiteData,
+ ClientCert,
+ ClientCertChain,
+ Close,
+ Connection,
+ ContentDigest,
+ ContentDisposition,
+ ContentEncoding,
+ ContentID,
+ ContentLanguage,
+ ContentLength,
+ ContentLocation,
+ ContentRange,
+ ContentSecurityPolicy,
+ ContentSecurityPolicyReportOnly,
+ ContentType,
+ Cookie,
+ CrossOriginEmbedderPolicy,
+ CrossOriginEmbedderPolicyReportOnly,
+ CrossOriginOpenerPolicy,
+ CrossOriginOpenerPolicyReportOnly,
+ CrossOriginResourcePolicy,
+ DASL,
+ Date,
+ DAV,
+ DeltaBase,
+ Depth,
+ Destination,
+ DifferentialID,
+ DPoP,
+ DPoPNonce,
+ EarlyData,
+ ETag,
+ Expect,
+ ExpectCT,
+ Expires,
+ Forwarded,
+ From,
+ Hobareg,
+ Host,
+ If,
+ IfMatch,
+ IfModifiedSince,
+ IfNoneMatch,
+ IfRange,
+ IfScheduleTagMatch,
+ IfUnmodifiedSince,
+ IM,
+ IncludeReferredTokenBindingID,
+ KeepAlive,
+ Label,
+ LastEventID,
+ LastModified,
+ Link,
+ Location,
+ LockToken,
+ MaxForwards,
+ MementoDatetime,
+ Meter,
+ MIMEVersion,
+ Negotiate,
+ NEL,
+ ODataEntityId,
+ ODataIsolation,
+ ODataMaxVersion,
+ ODataVersion,
+ OptionalWWWAuthenticate,
+ OrderingType,
+ Origin,
+ OriginAgentCluster,
+ OSCORE,
+ OSLCCoreVersion,
+ Overwrite,
+ PingFrom,
+ PingTo,
+ Position,
+ Prefer,
+ PreferenceApplied,
+ Priority,
+ ProxyAuthenticate,
+ ProxyAuthenticationInfo,
+ ProxyAuthorization,
+ ProxyStatus,
+ PublicKeyPins,
+ PublicKeyPinsReportOnly,
+ Range,
+ RedirectRef,
+ Referer,
+ Refresh,
+ ReplayNonce,
+ ReprDigest,
+ RetryAfter,
+ ScheduleReply,
+ ScheduleTag,
+ SecPurpose,
+ SecTokenBinding,
+ SecWebSocketAccept,
+ SecWebSocketExtensions,
+ SecWebSocketKey,
+ SecWebSocketProtocol,
+ SecWebSocketVersion,
+ Server,
+ ServerTiming,
+ SetCookie,
+ Signature,
+ SignatureInput,
+ SLUG,
+ SoapAction,
+ StatusURI,
+ StrictTransportSecurity,
+ Sunset,
+ SurrogateCapability,
+ SurrogateControl,
+ TCN,
+ TE,
+ Timeout,
+ Topic,
+ Traceparent,
+ Tracestate,
+ Trailer,
+ TransferEncoding,
+ TTL,
+ Upgrade,
+ Urgency,
+ UserAgent,
+ VariantVary,
+ Vary,
+ Via,
+ WantContentDigest,
+ WantReprDigest,
+ WWWAuthenticate,
+ XContentTypeOptions,
+ XFrameOptions,
+ // IANA Deprecated status:
+ AcceptCharset,
+ CPEPInfo,
+ Pragma,
+ ProtocolInfo,
+ ProtocolQuery,
+ };
+ Q_ENUM(WellKnownHeader)
+
+ Q_NETWORK_EXPORT QHttpHeaders() noexcept;
+ Q_NETWORK_EXPORT ~QHttpHeaders();
+
+ Q_NETWORK_EXPORT QHttpHeaders(const QHttpHeaders &other);
+ QHttpHeaders(QHttpHeaders &&other) noexcept = default;
+ Q_NETWORK_EXPORT QHttpHeaders &operator=(const QHttpHeaders &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttpHeaders)
+ void swap(QHttpHeaders &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT bool append(QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool append(WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool insert(qsizetype i, QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool insert(qsizetype i, WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool replace(qsizetype i, QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool replaceOrAppend(QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replaceOrAppend(WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool contains(QAnyStringView name) const;
+ Q_NETWORK_EXPORT bool contains(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT void clear();
+ Q_NETWORK_EXPORT void removeAll(QAnyStringView name);
+ Q_NETWORK_EXPORT void removeAll(WellKnownHeader name);
+ Q_NETWORK_EXPORT void removeAt(qsizetype i);
+
+ Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue = {}) const noexcept;
+ Q_NETWORK_EXPORT QByteArrayView value(WellKnownHeader name, QByteArrayView defaultValue = {}) const noexcept;
+
+ Q_NETWORK_EXPORT QList<QByteArray> values(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QList<QByteArray> values(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT QByteArrayView valueAt(qsizetype i) const noexcept;
+ Q_NETWORK_EXPORT QLatin1StringView nameAt(qsizetype i) const noexcept;
+
+ Q_NETWORK_EXPORT QByteArray combinedValue(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QByteArray combinedValue(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT qsizetype size() const noexcept;
+ Q_NETWORK_EXPORT void reserve(qsizetype size);
+ bool isEmpty() const noexcept { return size() == 0; }
+
+ Q_NETWORK_EXPORT static QByteArrayView wellKnownHeaderName(WellKnownHeader name) noexcept;
+
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers);
+
+ Q_NETWORK_EXPORT QList<std::pair<QByteArray, QByteArray>> toListOfPairs() const;
+ Q_NETWORK_EXPORT QMultiMap<QByteArray, QByteArray> toMultiMap() const;
+ Q_NETWORK_EXPORT QMultiHash<QByteArray, QByteArray> toMultiHash() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QHttpHeaders &headers);
+#endif
+ Q_ALWAYS_INLINE void verify([[maybe_unused]] qsizetype pos = 0,
+ [[maybe_unused]] qsizetype n = 1) const
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
+ QExplicitlySharedDataPointer<QHttpHeadersPrivate> d;
+};
+
+Q_DECLARE_SHARED(QHttpHeaders)
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERS_H
diff --git a/src/network/access/qhttpheadershelper.cpp b/src/network/access/qhttpheadershelper.cpp
new file mode 100644
index 0000000000..d3cc9e439f
--- /dev/null
+++ b/src/network/access/qhttpheadershelper.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttpheadershelper_p.h"
+
+#include <QtNetwork/qhttpheaders.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QHttpHeadersHelper::compareStrict(const QHttpHeaders &left, const QHttpHeaders &right)
+{
+ if (left.size() != right.size())
+ return false;
+
+ for (qsizetype i = 0; i < left.size(); ++i) {
+ if (left.nameAt(i) != right.nameAt(i))
+ return false;
+ if (left.valueAt(i) != right.valueAt(i))
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheadershelper_p.h b/src/network/access/qhttpheadershelper_p.h
new file mode 100644
index 0000000000..d1e38a1a8e
--- /dev/null
+++ b/src/network/access/qhttpheadershelper_p.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTPHEADERSHELPER_H
+#define QHTTPHEADERSHELPER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpHeaders;
+
+namespace QHttpHeadersHelper {
+ Q_NETWORK_EXPORT bool compareStrict(const QHttpHeaders &left, const QHttpHeaders &right);
+};
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERSHELPER_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index d6fefc4314..a695969f00 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpmultipart.h"
#include "qhttpmultipart_p.h"
@@ -353,11 +317,11 @@ void QHttpMultiPart::setBoundary(const QByteArray &boundary)
qint64 QHttpPartPrivate::bytesAvailable() const
{
checkHeaderCreated();
- qint64 bytesAvailable = header.count();
+ qint64 bytesAvailable = header.size();
if (bodyDevice) {
bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
} else {
- bytesAvailable += body.count() - readPointer;
+ bytesAvailable += body.size() - readPointer;
}
// the device might have closed etc., so make sure we do not return a negative value
return qMax(bytesAvailable, (qint64) 0);
@@ -367,7 +331,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
{
checkHeaderCreated();
qint64 bytesRead = 0;
- qint64 headerDataCount = header.count();
+ qint64 headerDataCount = header.size();
// read header if it has not been read yet
if (readPointer < headerDataCount) {
@@ -385,7 +349,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
bytesRead += dataBytesRead;
readPointer += dataBytesRead;
} else {
- qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, maxSize - bytesRead);
+ qint64 contentBytesRead = qMin(body.size() - readPointer + headerDataCount, maxSize - bytesRead);
const char *contentData = body.constData();
// if this method is called several times, we need to find the
// right offset in the content ourselves:
@@ -400,11 +364,11 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
qint64 QHttpPartPrivate::size() const
{
checkHeaderCreated();
- qint64 size = header.count();
+ qint64 size = header.size();
if (bodyDevice) {
size += bodyDevice->size();
} else {
- size += body.count();
+ size += body.size();
}
return size;
}
@@ -422,10 +386,12 @@ void QHttpPartPrivate::checkHeaderCreated() const
{
if (!headerCreated) {
// copied from QHttpNetworkRequestPrivate::header() and adapted
- QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- for (; it != fields.constEnd(); ++it)
- header += it->first + ": " + it->second + "\r\n";
+ const auto h = headers();
+ for (qsizetype i = 0; i < h.size(); ++i) {
+ const auto name = h.nameAt(i);
+ header += QByteArrayView(name.data(), name.size()) + ": " + h.valueAt(i) + "\r\n";
+ }
+
header += "\r\n";
headerCreated = true;
}
@@ -440,7 +406,7 @@ QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::Mix
+ QByteArray::fromRawData(reinterpret_cast<char *>(random), sizeof(random)).toBase64();
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
- Q_ASSERT(boundary.count() <= 70);
+ Q_ASSERT(boundary.size() <= 70);
}
qint64 QHttpMultiPartIODevice::size() const
@@ -449,8 +415,8 @@ qint64 QHttpMultiPartIODevice::size() const
// including boundary (needed later in readData)
if (deviceSize == -1) {
qint64 currentSize = 0;
- qint64 boundaryCount = multiPart->boundary.count();
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ qint64 boundaryCount = multiPart->boundary.size();
+ for (int a = 0; a < multiPart->parts.size(); a++) {
partOffsets.append(currentSize);
// 4 additional bytes for the "--" before and the "\r\n" after the boundary,
// and 2 bytes for the "\r\n" after the content
@@ -464,7 +430,7 @@ qint64 QHttpMultiPartIODevice::size() const
bool QHttpMultiPartIODevice::isSequential() const
{
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ for (int a = 0; a < multiPart->parts.size(); a++) {
QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
// we are sequential if any of the bodyDevices of our parts are sequential;
// when reading from a byte array, we are not sequential
@@ -478,7 +444,7 @@ bool QHttpMultiPartIODevice::reset()
{
// Reset QIODevice's data
QIODevice::reset();
- for (int a = 0; a < multiPart->parts.count(); a++)
+ for (int a = 0; a < multiPart->parts.size(); a++)
if (!multiPart->parts[a].d->reset())
return false;
readPointer = 0;
@@ -489,17 +455,17 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
qint64 bytesRead = 0, index = 0;
// skip the parts we have already read
- while (index < multiPart->parts.count() &&
+ while (index < multiPart->parts.size() &&
readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size()
- + multiPart->boundary.count() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
+ + multiPart->boundary.size() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
index++;
// read the data
- while (bytesRead < maxSize && index < multiPart->parts.count()) {
+ while (bytesRead < maxSize && index < multiPart->parts.size()) {
// check whether we need to read the boundary of the current part
QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
- qint64 boundaryCount = boundaryData.count();
+ qint64 boundaryCount = boundaryData.size();
qint64 partIndex = readPointer - partOffsets.at(index);
if (partIndex < boundaryCount) {
qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
@@ -530,10 +496,10 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
}
}
// check whether we need to return the final boundary
- if (bytesRead < maxSize && index == multiPart->parts.count()) {
+ if (bytesRead < maxSize && index == multiPart->parts.size()) {
QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
- qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
- qint64 lastBoundaryBytesRead = qMin(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
+ qint64 boundaryIndex = readPointer + finalBoundary.size() - size();
+ qint64 lastBoundaryBytesRead = qMin(finalBoundary.size() - boundaryIndex, maxSize - bytesRead);
memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
bytesRead += lastBoundaryBytesRead;
readPointer += lastBoundaryBytesRead;
@@ -548,5 +514,49 @@ qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
return -1;
}
+#ifndef QT_NO_DEBUG_STREAM
+
+/*!
+ \fn QDebug QHttpPart::operator<<(QDebug debug, const QHttpPart &part)
+
+ Writes the \a part into the \a debug object for debugging purposes.
+ Unless a device is set, the size of the body is shown.
+
+ \sa {Debugging Techniques}
+ \since 6.8
+*/
+
+QDebug operator<<(QDebug debug, const QHttpPart &part)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace().noquote();
+
+ debug << "QHttpPart(headers = ["
+ << part.d->cookedHeaders
+ << "], http headers = ["
+ << part.d->httpHeaders
+ << "],";
+
+ if (part.d->bodyDevice) {
+ debug << " bodydevice = ["
+ << part.d->bodyDevice
+ << ", is open: "
+ << part.d->bodyDevice->isOpen()
+ << "]";
+ } else {
+ debug << " size of body = "
+ << part.d->body.size()
+ << " bytes";
+ }
+
+ debug << ")";
+
+ return debug;
+}
+
+#endif // QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
+
+#include "moc_qhttpmultipart.cpp"
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
index 56db83779a..9732bbd99d 100644
--- a/src/network/access/qhttpmultipart.h
+++ b/src/network/access/qhttpmultipart.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPMULTIPART_H
#define QHTTPMULTIPART_H
@@ -46,13 +10,16 @@
#include <QtCore/QIODevice>
#include <QtNetwork/QNetworkRequest>
+#ifndef Q_OS_WASM
QT_REQUIRE_CONFIG(http);
+#endif
QT_BEGIN_NAMESPACE
class QHttpPartPrivate;
class QHttpMultiPart;
+class QDebug;
class Q_NETWORK_EXPORT QHttpPart
{
@@ -63,7 +30,7 @@ public:
QHttpPart &operator=(QHttpPart &&other) noexcept { swap(other); return *this; }
QHttpPart &operator=(const QHttpPart &other);
- void swap(QHttpPart &other) noexcept { qSwap(d, other.d); }
+ void swap(QHttpPart &other) noexcept { d.swap(other.d); }
bool operator==(const QHttpPart &other) const;
inline bool operator!=(const QHttpPart &other) const
@@ -79,6 +46,9 @@ private:
QSharedDataPointer<QHttpPartPrivate> d;
friend class QHttpMultiPartIODevice;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QHttpPart &httpPart);
+#endif
};
Q_DECLARE_SHARED(QHttpPart)
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
index ead1eadf3b..7a12ce8424 100644
--- a/src/network/access/qhttpmultipart_p.h
+++ b/src/network/access/qhttpmultipart_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPMULTIPART_P_H
#define QHTTPMULTIPART_P_H
@@ -54,9 +18,12 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qshareddata.h"
#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
+#include "qhttpheadershelper_p.h"
#include "private/qobject_p.h"
+#ifndef Q_OS_WASM
QT_REQUIRE_CONFIG(http);
+#endif
QT_BEGIN_NAMESPACE
@@ -81,8 +48,10 @@ public:
inline bool operator==(const QHttpPartPrivate &other) const
{
- return rawHeaders == other.rawHeaders && body == other.body &&
- bodyDevice == other.bodyDevice && readPointer == other.readPointer;
+ return QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
+ && body == other.body
+ && bodyDevice == other.bodyDevice
+ && readPointer == other.readPointer;
}
void setBodyDevice(QIODevice *device) {
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index b9a4c874c0..3ef07c6993 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkconnection_p.h"
#include <private/qabstractsocket_p.h>
@@ -48,10 +12,14 @@
#include <qnetworkproxy.h>
#include <qauthenticator.h>
#include <qcoreapplication.h>
+#include <private/qdecompresshelper_p.h>
+#include <private/qsocketabstraction_p.h>
#include <qbuffer.h>
#include <qpair.h>
#include <qdebug.h>
+#include <qspan.h>
+#include <qvarlengtharray.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@@ -65,7 +33,7 @@
QT_BEGIN_NAMESPACE
-const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
+using namespace Qt::StringLiterals;
// The pipeline length. So there will be 4 requests in flight.
const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
@@ -73,46 +41,36 @@ const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
+static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType type,
+ int defaultValue)
+{
+ return (type == QHttpNetworkConnection::ConnectionTypeHTTP2
+ || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct)
+ ? 1
+ : defaultValue;
+}
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState),
- networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
- , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2
- || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
-#ifndef QT_NO_SSL
- || type == QHttpNetworkConnection::ConnectionTypeSPDY
-#endif
- ? 1 : defaultHttpChannelCount)
- , channelCount(defaultHttpChannelCount)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
+ quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
+ bool isLocalSocket, QHttpNetworkConnection::ConnectionType type)
+ : hostName(hostName),
+ port(port),
+ encrypt(encrypt),
+ isLocalSocket(isLocalSocket),
+ activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
+ channelCount(connectionCount),
+ channels(new QHttpNetworkConnectionChannel[channelCount]),
#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
+ networkProxy(QNetworkProxy::NoProxy),
#endif
- , preConnectRequests(0)
- , connectionType(type)
+ connectionType(type)
{
- // We allocate all 6 channels even if it's SPDY or HTTP/2 enabled
+ if (isLocalSocket) // Don't try to do host lookup for local sockets
+ networkLayerState = IPv4;
+ // We allocate all 6 channels even if it's an HTTP/2-enabled
// connection: in case the protocol negotiation via NPN/ALPN fails,
// we will have normally working HTTP/1.1.
Q_ASSERT(channelCount >= activeChannelCount);
- channels = new QHttpNetworkConnectionChannel[channelCount];
-}
-
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState), networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- activeChannelCount(connectionCount), channelCount(connectionCount)
-#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
-#endif
- , preConnectRequests(0)
- , connectionType(type)
-{
- channels = new QHttpNetworkConnectionChannel[channelCount];
}
@@ -135,10 +93,6 @@ void QHttpNetworkConnectionPrivate::init()
for (int i = 0; i < channelCount; i++) {
channels[i].setConnection(this->q_func());
channels[i].ssl = encrypt;
-#ifndef QT_NO_BEARERMANAGEMENT
- //push session down to channels
- channels[i].networkSession = networkSession;
-#endif
}
delayedConnectionTimer.setSingleShot(true);
@@ -151,13 +105,18 @@ void QHttpNetworkConnectionPrivate::pauseConnection()
// Disable all socket notifiers
for (int i = 0; i < activeChannelCount; i++) {
- if (channels[i].socket) {
+ if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
#ifndef QT_NO_SSL
if (encrypt)
- QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(absSocket));
else
#endif
- QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
+ QAbstractSocketPrivate::pauseSocketNotifiers(absSocket);
+ } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
+ // @todo how would we do this?
+#if 0 // @todo Enable this when there is a debug category for this
+ qDebug() << "Should pause socket but there is no way to do it for local sockets";
+#endif
}
}
}
@@ -167,17 +126,21 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
state = RunningState;
// Enable all socket notifiers
for (int i = 0; i < activeChannelCount; i++) {
- if (channels[i].socket) {
+ if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
#ifndef QT_NO_SSL
if (encrypt)
- QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(absSocket));
else
#endif
- QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
+ QAbstractSocketPrivate::resumeSocketNotifiers(absSocket);
// Resume pending upload if needed
if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
+ } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
+#if 0 // @todo Enable this when there is a debug category for this
+ qDebug() << "Should resume socket but there is no way to do it for local sockets";
+#endif
}
}
@@ -185,7 +148,7 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
}
-int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
+int QHttpNetworkConnectionPrivate::indexOf(QIODevice *socket) const
{
for (int i = 0; i < activeChannelCount; ++i)
if (channels[i].socket == socket)
@@ -199,7 +162,7 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
// emitted. This function will check the status of the connection channels if we
// have not decided the networkLayerState and will return true if the channel error
// should be emitted by the channel.
-bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QIODevice *socket)
{
Q_Q(QHttpNetworkConnection);
@@ -257,6 +220,17 @@ qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const
return reply.d_func()->responseData.sizeNextBlock();
}
+static QByteArray makeAcceptLanguage()
+{
+ QString systemLocale = QLocale::system().name();
+ if (systemLocale == "C"_L1)
+ return "en,*"_ba;
+ systemLocale.replace('_'_L1, '-'_L1);
+ if (systemLocale.startsWith("en-"_L1))
+ return (systemLocale + ",*"_L1).toLatin1();
+ return (systemLocale + ",en,*"_L1).toLatin1();
+}
+
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
{
QHttpNetworkRequest &request = messagePair.first;
@@ -264,14 +238,16 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
// add missing fields for the request
QByteArray value;
+#ifndef Q_OS_WASM
// check if Content-Length is provided
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
if (uploadByteDevice) {
const qint64 contentLength = request.contentLength();
const qint64 uploadDeviceSize = uploadByteDevice->size();
if (contentLength != -1 && uploadDeviceSize != -1) {
- // both values known, take the smaller one.
- request.setContentLength(qMin(uploadDeviceSize, contentLength));
+ // Both values known: use the smaller one.
+ if (uploadDeviceSize < contentLength)
+ request.setContentLength(uploadDeviceSize);
} else if (contentLength == -1 && uploadDeviceSize != -1) {
// content length not supplied by user, but the upload device knows it
request.setContentLength(uploadDeviceSize);
@@ -281,6 +257,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
}
}
+#endif
// set the Connection/Proxy-Connection: Keep-Alive headers
#ifndef QT_NO_NETWORKPROXY
if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
@@ -304,7 +281,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
value = request.headerField("accept-encoding");
if (value.isEmpty()) {
#ifndef QT_NO_COMPRESS
- request.setHeaderField("Accept-Encoding", "gzip, deflate");
+ const static QByteArray acceptedEncoding = QDecompressHelper::acceptedEncoding().join(", ");
+ request.setHeaderField("Accept-Encoding", acceptedEncoding);
request.d->autoDecompress = true;
#else
// if zlib is not available set this to false always
@@ -317,17 +295,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
// not with us, but we work around this by setting
// one always.
value = request.headerField("accept-language");
- if (value.isEmpty()) {
- QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
- QString acceptLanguage;
- if (systemLocale == QLatin1String("C"))
- acceptLanguage = QString::fromLatin1("en,*");
- else if (systemLocale.startsWith(QLatin1String("en-")))
- acceptLanguage = systemLocale + QLatin1String(",*");
- else
- acceptLanguage = systemLocale + QLatin1String(",en,*");
- request.setHeaderField("Accept-Language", std::move(acceptLanguage).toLatin1());
- }
+ if (value.isEmpty())
+ request.setHeaderField("Accept-Language", makeAcceptLanguage());
// set the User Agent
value = request.headerField("user-agent");
@@ -335,12 +304,17 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
request.setHeaderField("User-Agent", "Mozilla/5.0");
// set the host
value = request.headerField("host");
- if (value.isEmpty()) {
+ if (isLocalSocket && value.isEmpty()) {
+ // The local socket connections might have a full file path, and that
+ // may not be suitable for the Host header. But we can use whatever the
+ // user has set in the URL.
+ request.prependHeaderField("Host", request.url().host().toLocal8Bit());
+ } else if (value.isEmpty()) {
QHostAddress add;
QByteArray host;
if (add.setAddress(hostName)) {
if (add.protocol() == QAbstractSocket::IPv6Protocol)
- host = '[' + hostName.toLatin1() + ']'; //format the ipv6 in the standard way
+ host = (u'[' + hostName + u']').toLatin1(); //format the ipv6 in the standard way
else
host = hostName.toLatin1();
@@ -363,7 +337,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
-void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
+void QHttpNetworkConnectionPrivate::emitReplyError(QIODevice *socket,
QHttpNetworkReply *reply,
QNetworkReply::NetworkError errorCode)
{
@@ -401,10 +375,12 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
// NTLM and Negotiate do multi-phase authentication.
// Copying credentialsbetween authenticators would mess things up.
if (fromChannel >= 0) {
- const QHttpNetworkConnectionChannel &channel = channels[fromChannel];
- const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod;
- if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate)
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
+ if (priv
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate)) {
return;
+ }
}
// select another channel
@@ -426,7 +402,7 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
// handles the authentication for one channel and eventually re-starts the other channels
-bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
+bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QIODevice *socket, QHttpNetworkReply *reply,
bool isProxy, bool &resend)
{
Q_ASSERT(socket);
@@ -434,28 +410,34 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
resend = false;
//create the response header to be used with QAuthenticatorPrivate.
- QList<QPair<QByteArray, QByteArray> > fields = reply->header();
-
- //find out the type of authentication protocol requested.
- QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
- if (authMethod != QAuthenticatorPrivate::None) {
+ const auto headers = reply->header();
+
+ // Check that any of the proposed authenticate methods are supported
+ const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
+ const QByteArrayList &authenticationMethods = reply->d_func()->headerFieldValues(header);
+ const bool isSupported = std::any_of(authenticationMethods.begin(), authenticationMethods.end(),
+ QAuthenticatorPrivate::isMethodSupported);
+ if (isSupported) {
int i = indexOf(socket);
//Use a single authenticator for all domains. ### change later to use domain/realm
- QAuthenticator* auth = nullptr;
- if (isProxy) {
- auth = &channels[i].proxyAuthenticator;
- channels[i].proxyAuthMethod = authMethod;
- } else {
- auth = &channels[i].authenticator;
- channels[i].authMethod = authMethod;
- }
+ QAuthenticator *auth = isProxy ? &channels[i].proxyAuthenticator
+ : &channels[i].authenticator;
//proceed with the authentication.
if (auth->isNull())
auth->detach();
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
- priv->parseHttpResponse(fields, isProxy);
+ priv->parseHttpResponse(headers, isProxy, reply->url().host());
+ // Update method in case it changed
+ if (priv->method == QAuthenticatorPrivate::None)
+ return false;
+
+ if (priv->phase == QAuthenticatorPrivate::Done ||
+ (priv->phase == QAuthenticatorPrivate::Start
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate))) {
+ if (priv->phase == QAuthenticatorPrivate::Start)
+ priv->phase = QAuthenticatorPrivate::Phase1;
- if (priv->phase == QAuthenticatorPrivate::Done) {
pauseConnection();
if (!isProxy) {
if (channels[i].authenticationCredentialsSent) {
@@ -503,8 +485,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
channels[i].authenticator = QAuthenticator();
// authentication is cancelled, send the current contents to the user.
- emit channels[i].reply->headerChanged();
- emit channels[i].reply->readyRead();
+ emit reply->headerChanged();
+ emit reply->readyRead();
QNetworkReply::NetworkError errorCode =
isProxy
? QNetworkReply::ProxyAuthenticationRequiredError
@@ -521,31 +503,37 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
return false;
}
-QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply)
+// Used by the HTTP1 code-path
+QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QIODevice *socket,
+ QHttpNetworkReply *reply)
+{
+ ParseRedirectResult result = parseRedirectResponse(reply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ emitReplyError(socket, reply, result.errorCode);
+ return {};
+ }
+ return std::move(result.redirectUrl);
+}
+
+QHttpNetworkConnectionPrivate::ParseRedirectResult
+QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
{
if (!reply->request().isFollowRedirects())
- return QUrl();
+ return {{}, QNetworkReply::NoError};
QUrl redirectUrl;
- const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
- for (const QNetworkReply::RawHeaderPair &header : fields) {
- if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
- redirectUrl = QUrl::fromEncoded(header.second);
- break;
- }
+ const QHttpHeaders fields = reply->header();
+ if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
+ redirectUrl = QUrl::fromEncoded(h.first());
}
- // If the location url is invalid/empty, we emit ProtocolUnknownError
- if (!redirectUrl.isValid()) {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
- }
+ // If the location url is invalid/empty, we return ProtocolUnknownError
+ if (!redirectUrl.isValid())
+ return {{}, QNetworkReply::ProtocolUnknownError};
// Check if we have exceeded max redirects allowed
- if (reply->request().redirectCount() <= 0) {
- emitReplyError(socket, reply, QNetworkReply::TooManyRedirectsError);
- return QUrl();
- }
+ if (reply->request().redirectCount() <= 0)
+ return {{}, QNetworkReply::TooManyRedirectsError};
// Resolve the URL if it's relative
if (redirectUrl.isRelative())
@@ -553,7 +541,9 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
// Check redirect url protocol
const QUrl priorUrl(reply->request().url());
- if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
+ const QString targetUrlScheme = redirectUrl.scheme();
+ if (targetUrlScheme == "http"_L1 || targetUrlScheme == "https"_L1
+ || targetUrlScheme.startsWith("unix"_L1)) {
switch (reply->request().redirectPolicy()) {
case QNetworkRequest::NoLessSafeRedirectPolicy:
// Here we could handle https->http redirects as InsecureProtocolError.
@@ -564,10 +554,9 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
break;
case QNetworkRequest::SameOriginRedirectPolicy:
if (priorUrl.host() != redirectUrl.host()
- || priorUrl.scheme() != redirectUrl.scheme()
+ || priorUrl.scheme() != targetUrlScheme
|| priorUrl.port() != redirectUrl.port()) {
- emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
- return QUrl();
+ return {{}, QNetworkReply::InsecureRedirectError};
}
break;
case QNetworkRequest::UserVerifiedRedirectPolicy:
@@ -576,40 +565,53 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
Q_ASSERT(!"Unexpected redirect policy");
}
} else {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
+ return {{}, QNetworkReply::ProtocolUnknownError};
}
- return redirectUrl;
+ return {std::move(redirectUrl), QNetworkReply::NoError};
}
-void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
+void QHttpNetworkConnectionPrivate::createAuthorization(QIODevice *socket, QHttpNetworkRequest &request)
{
Q_ASSERT(socket);
- int i = indexOf(socket);
+ QHttpNetworkConnectionChannel &channel = channels[indexOf(socket)];
+ QAuthenticator *authenticator = &channel.authenticator;
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
- if (channels[i].authMethod != QAuthenticatorPrivate::None) {
- if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) {
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
- if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host());
- request.setHeaderField("Authorization", response);
- channels[i].authenticationCredentialsSent = true;
- }
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate;
+ const bool authNeeded = channel.lastStatus == 401;
+ const bool ntlmNegoOk = ntlmNego && authNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done
+ || !channel.authenticationCredentialsSent);
+ const bool otherOk =
+ !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
+ if (ntlmNegoOk || otherOk) {
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
+ request.url().host());
+ request.setHeaderField("Authorization", response);
+ channel.authenticationCredentialsSent = true;
}
}
#if QT_CONFIG(networkproxy)
+ authenticator = &channel.proxyAuthenticator;
+ priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
- if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
- if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
- if (priv && priv->method != QAuthenticatorPrivate::None) {
- QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName());
- request.setHeaderField("Proxy-Authorization", response);
- channels[i].proxyCredentialsSent = true;
- }
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate;
+ const bool proxyAuthNeeded = channel.lastStatus == 407;
+ const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
+ const bool otherOk = !ntlmNego;
+ if (ntlmNegoOk || otherOk) {
+ QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
+ networkProxy.hostName());
+ request.setHeaderField("Proxy-Authorization", response);
+ channel.proxyCredentialsSent = true;
}
}
#endif // QT_CONFIG(networkproxy)
@@ -641,13 +643,12 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
break;
}
}
- else { // SPDY, HTTP/2 ('h2' mode)
+ else { // HTTP/2 ('h2' mode)
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insert(request.priority(), pair);
+ channels[0].h2RequestsToSend.insert(request.priority(), pair);
}
-#ifndef Q_OS_WINRT
// For Happy Eyeballs the networkLayerState is set to Unknown
// until we have started the first connection attempt. So no
// request will be started until we know if IPv4 or IPv6
@@ -655,13 +656,6 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
if (networkLayerState == Unknown || networkLayerState == HostLookupPending) {
startHostInfoLookup();
} else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
-#else // !Q_OS_WINRT
- {
- // Skip the host lookup part for winrt. Host lookup and proxy handling are done by Windows
- // internally and networkLayerPreference is ignored on this platform. Instead of refactoring
- // the whole approach we just pretend that everything important is known here.
- networkLayerState = IPv4;
-#endif
// this used to be called via invokeMethod and a QueuedConnection
// It is the only place _q_startNextRequest is called directly without going
// through the event loop using a QueuedConnection.
@@ -680,7 +674,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue()
for (auto &pair : highPriorityQueue) {
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair);
+ channels[0].h2RequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair);
}
highPriorityQueue.clear();
@@ -688,7 +682,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue()
for (auto &pair : lowPriorityQueue) {
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
- channels[0].spdyRequestsToSend.insert(pair.first.priority(), pair);
+ channels[0].h2RequestsToSend.insert(pair.first.priority(), pair);
}
lowPriorityQueue.clear();
@@ -712,7 +706,7 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
-bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QIODevice *socket)
{
int i = 0;
if (socket)
@@ -756,8 +750,17 @@ QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest() const
return QHttpNetworkRequest();
}
+QHttpNetworkReply* QHttpNetworkConnectionPrivate::predictNextRequestsReply() const
+{
+ if (!highPriorityQueue.isEmpty())
+ return highPriorityQueue.last().second;
+ if (!lowPriorityQueue.isEmpty())
+ return lowPriorityQueue.last().second;
+ return nullptr;
+}
+
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
-void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
+void QHttpNetworkConnectionPrivate::fillPipeline(QIODevice *socket)
{
// return fast if there is nothing to pipeline
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
@@ -769,7 +772,7 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
if (channels[i].reply == nullptr)
return;
- if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
+ if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
return;
}
@@ -785,7 +788,7 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
return;
// check if socket is connected
- if (socket->state() != QAbstractSocket::ConnectedState)
+ if (QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState)
return;
// check for resendCurrent
@@ -810,28 +813,28 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything, now do the low prio queue
}
while (!lowPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything
}
@@ -845,7 +848,7 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue,
if (queue.isEmpty())
return true;
- for (int i = queue.count() - 1; i >= 0; --i) {
+ for (int i = queue.size() - 1; i >= 0; --i) {
HttpMessagePair messagePair = queue.at(i);
const QHttpNetworkRequest &request = messagePair.first;
@@ -879,16 +882,15 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue,
}
-QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
+QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QIODevice *socket, const QString &extraDetail)
{
QString errorString;
switch (errorCode) {
- case QNetworkReply::HostNotFoundError:
- if (socket)
- errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
- else
- errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
+ case QNetworkReply::HostNotFoundError: {
+ const QString peerName = socket ? QSocketAbstraction::socketPeerName(socket) : hostName;
+ errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(peerName);
break;
+ }
case QNetworkReply::ConnectionRefusedError:
errorString = QCoreApplication::translate("QHttp", "Connection refused");
break;
@@ -912,6 +914,8 @@ QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError e
break;
case QNetworkReply::SslHandshakeFailedError:
errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
+ if (socket)
+ errorString += ": "_L1 + socket->errorString();
break;
case QNetworkReply::TooManyRedirectsError:
errorString = QCoreApplication::translate("QHttp", "Too many redirects");
@@ -965,7 +969,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// is the reply inside the pipeline of this channel already?
- for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
+ for (int j = 0; j < channels[i].alreadyPipelinedRequests.size(); j++) {
if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
// Remove that HttpMessagePair
channels[i].alreadyPipelinedRequests.removeAt(j);
@@ -983,23 +987,22 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
return;
}
}
-#ifndef QT_NO_SSL
- // is the reply inside the SPDY pipeline of this channel already?
- QMultiMap<int, HttpMessagePair>::iterator it = channels[i].spdyRequestsToSend.begin();
- QMultiMap<int, HttpMessagePair>::iterator end = channels[i].spdyRequestsToSend.end();
- for (; it != end; ++it) {
- if (it.value().second == reply) {
- channels[i].spdyRequestsToSend.remove(it.key());
-
- QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
- return;
- }
+ // is the reply inside the H2 pipeline of this channel already?
+ const auto foundReply = [reply](const HttpMessagePair &pair) {
+ return pair.second == reply;
+ };
+ auto &seq = channels[i].h2RequestsToSend;
+ const auto end = seq.cend();
+ auto it = std::find_if(seq.cbegin(), end, foundReply);
+ if (it != end) {
+ seq.erase(it);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
}
-#endif
}
// remove from the high priority queue
if (!highPriorityQueue.isEmpty()) {
- for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = highPriorityQueue.at(j);
if (messagePair.second == reply) {
highPriorityQueue.removeAt(j);
@@ -1010,7 +1013,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// remove from the low priority queue
if (!lowPriorityQueue.isEmpty()) {
- for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = lowPriorityQueue.at(j);
if (messagePair.second == reply) {
lowPriorityQueue.removeAt(j);
@@ -1039,6 +1042,11 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
//resend the necessary ones.
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
+ if (!channels[i].socket
+ || QSocketAbstraction::socketState(channels[i].socket) == QAbstractSocket::UnconnectedState) {
+ if (!channels[i].ensureConnection())
+ continue;
+ }
channels[i].resendCurrent = false;
// if this is not possible, error will be emitted and connection terminated
@@ -1059,7 +1067,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// try to get a free AND connected socket
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].socket) {
- if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
+ if (!channels[i].reply && !channels[i].isSocketBusy()
+ && QSocketAbstraction::socketState(channels[i].socket)
+ == QAbstractSocket::ConnectedState) {
if (dequeueRequest(channels[i].socket))
channels[i].sendRequest();
}
@@ -1068,19 +1078,31 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
break;
}
case QHttpNetworkConnection::ConnectionTypeHTTP2Direct:
- case QHttpNetworkConnection::ConnectionTypeHTTP2:
- case QHttpNetworkConnection::ConnectionTypeSPDY: {
- if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2)
+ case QHttpNetworkConnection::ConnectionTypeHTTP2: {
+ if (channels[0].h2RequestsToSend.isEmpty() && !channels[0].reply
+ && highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) {
return;
+ }
if (networkLayerState == IPv4)
channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
else if (networkLayerState == IPv6)
channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
channels[0].ensureConnection();
- if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
- && !channels[0].pendingEncrypt && channels[0].spdyRequestsToSend.size())
- channels[0].sendRequest();
+ if (auto *s = channels[0].socket; s
+ && QSocketAbstraction::socketState(s) == QAbstractSocket::ConnectedState
+ && !channels[0].pendingEncrypt) {
+ if (channels[0].h2RequestsToSend.size()) {
+ channels[0].sendRequest();
+ } else if (!channels[0].reply && !channels[0].switchedToHttp2) {
+ // This covers an edge-case where we're already connected and the "connected"
+ // signal was already sent, but we didn't have any request available at the time,
+ // so it was missed. As such we need to dequeue a request and send it now that we
+ // have one.
+ dequeueRequest(channels[0].socket);
+ channels[0].sendRequest();
+ }
+ }
break;
}
}
@@ -1095,14 +1117,18 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// return fast if there is nothing to pipeline
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
return;
- for (int i = 0; i < activeChannelCount; i++)
- if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
+ for (int i = 0; i < activeChannelCount; i++) {
+ if (channels[i].socket
+ && QSocketAbstraction::socketState(channels[i].socket)
+ == QAbstractSocket::ConnectedState) {
fillPipeline(channels[i].socket);
+ }
+ }
// If there is not already any connected channels we need to connect a new one.
// We do not pair the channel with the request until we know if it is
// connected or not. This is to reuse connected channels before we connect new once.
- int queuedRequests = highPriorityQueue.count() + lowPriorityQueue.count();
+ int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
// in case we have in-flight preconnect requests and normal requests,
// we only need one socket for each (preconnect, normal request) pair
@@ -1111,31 +1137,53 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
int normalRequests = queuedRequests - preConnectRequests;
neededOpenChannels = qMax(normalRequests, preConnectRequests);
}
+
+ if (neededOpenChannels <= 0)
+ return;
+
+ QVarLengthArray<int> channelsToConnect;
+
+ // use previously used channels first
for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
- bool connectChannel = false;
- if (channels[i].socket) {
- if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
- || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
- || channels[i].pendingEncrypt) // pendingEncrypt == "EncryptingState"
- neededOpenChannels--;
-
- if (neededOpenChannels <= 0)
- break;
- if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState))
- connectChannel = true;
- } else { // not previously used channel
- connectChannel = true;
+ if (!channels[i].socket)
+ continue;
+
+ using State = QAbstractSocket::SocketState;
+ if ((QSocketAbstraction::socketState(channels[i].socket) == State::ConnectingState)
+ || (QSocketAbstraction::socketState(channels[i].socket) == State::HostLookupState)
+ || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
+ neededOpenChannels--;
+ continue;
}
- if (connectChannel) {
- if (networkLayerState == IPv4)
- channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol;
- else if (networkLayerState == IPv6)
- channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol;
- channels[i].ensureConnection();
+ if (!channels[i].reply && !channels[i].isSocketBusy()
+ && (QSocketAbstraction::socketState(channels[i].socket) == State::UnconnectedState)) {
+ channelsToConnect.push_back(i);
neededOpenChannels--;
}
}
+
+ // use other channels
+ for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
+ if (channels[i].socket)
+ continue;
+
+ channelsToConnect.push_back(i);
+ neededOpenChannels--;
+ }
+
+ auto channelToConnectSpan = QSpan{channelsToConnect};
+ while (!channelToConnectSpan.isEmpty()) {
+ const int channel = channelToConnectSpan.front();
+ channelToConnectSpan = channelToConnectSpan.sliced(1);
+
+ if (networkLayerState == IPv4)
+ channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol;
+ else if (networkLayerState == IPv6)
+ channels[channel].networkLayerPreference = QAbstractSocket::IPv6Protocol;
+
+ channels[channel].ensureConnection();
+ }
}
@@ -1231,24 +1279,30 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
} else {
+ auto lookupError = QNetworkReply::HostNotFoundError;
+#ifndef QT_NO_NETWORKPROXY
+ // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
+ // proxy hostname are delegated to the proxy.
+ auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
+ if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
+ lookupError = QNetworkReply::ProxyNotFoundError;
+#endif
if (dequeueRequest(channels[0].socket)) {
- emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, channels[0].reply, lookupError);
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
- } else if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY
- || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
+ } else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- for (const HttpMessagePair &spdyPair : qAsConst(channels[0].spdyRequestsToSend)) {
+ for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
// emit error for all replies
- QHttpNetworkReply *currentReply = spdyPair.second;
+ QHttpNetworkReply *currentReply = h2Pair.second;
Q_ASSERT(currentReply);
- emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, currentReply, lookupError);
}
} else {
- // Should not happen: we start a host lookup before sending a request,
- // so it's natural to have requests either in SPDY/HTTP/2 queue,
- // or in low/high priority queues.
- qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
- " could not de-queue request, failed to report HostNotFoundError");
+ // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
+ // before the host lookup was finished.
+ qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
+ " could not de-queue request, failed to report HostNotFoundError");
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
}
}
@@ -1272,19 +1326,6 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
int timeout = 300;
-#ifndef QT_NO_BEARERMANAGEMENT
- if (networkSession) {
- const QNetworkConfiguration::BearerType bearerType = networkSession->configuration().bearerType();
- if (bearerType == QNetworkConfiguration::Bearer2G)
- timeout = 800;
- else if (bearerType == QNetworkConfiguration::BearerCDMA2000)
- timeout = 500;
- else if (bearerType == QNetworkConfiguration::BearerWCDMA)
- timeout = 500;
- else if (bearerType == QNetworkConfiguration::BearerHSPA)
- timeout = 400;
- }
-#endif
delayedConnectionTimer.start(timeout);
if (delayIpv4)
channels[1].ensureConnection();
@@ -1314,64 +1355,20 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
channels[1].ensureConnection();
}
-#ifndef QT_NO_BEARERMANAGEMENT
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType connectionType,
- QObject *parent, QSharedPointer<QNetworkSession> networkSession)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent)
-{
- Q_D(QHttpNetworkConnection);
- d->networkSession = std::move(networkSession);
- d->init();
- if (QNetworkStatusMonitor::isEnabled()) {
- connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
- this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
- }
-}
-
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt, QObject *parent,
- QSharedPointer<QNetworkSession> networkSession,
+ quint16 port, bool encrypt, bool isLocalSocket, QObject *parent,
QHttpNetworkConnection::ConnectionType connectionType)
- : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket,
connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
- d->networkSession = std::move(networkSession);
- d->init();
- if (QNetworkStatusMonitor::isEnabled()) {
- connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
- this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
- }
-}
-#else
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType connectionType, QObject *parent)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
-{
- Q_D(QHttpNetworkConnection);
d->init();
- if (QNetworkStatusMonitor::isEnabled()) {
+ if (QNetworkConnectionMonitor::isEnabled()) {
connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
}
}
-QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt, QObject *parent,
- QHttpNetworkConnection::ConnectionType connectionType)
- : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
- connectionType)), parent)
-{
- Q_D(QHttpNetworkConnection);
- d->init();
- if (QNetworkStatusMonitor::isEnabled()) {
- connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
- this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
- }
-}
-#endif // QT_NO_BEARERMANAGEMENT
-
QHttpNetworkConnection::~QHttpNetworkConnection()
{
}
@@ -1418,7 +1415,7 @@ void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
d->networkProxy = networkProxy;
// update the authenticator
if (!d->networkProxy.user().isEmpty()) {
- for (int i = 0; i < d->activeChannelCount; ++i) {
+ for (int i = 0; i < d->channelCount; ++i) {
d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
}
@@ -1434,7 +1431,7 @@ QNetworkProxy QHttpNetworkConnection::cacheProxy() const
void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
{
Q_D(QHttpNetworkConnection);
- for (int i = 0; i < d->activeChannelCount; ++i)
+ for (int i = 0; i < d->channelCount; ++i)
d->channels[i].setProxy(networkProxy);
}
@@ -1445,9 +1442,9 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
-QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
+QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->connectionType;
}
@@ -1482,13 +1479,13 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config
d->channels[i].setSslConfiguration(config);
}
-QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext()
+std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->sslContext;
}
-void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context)
+void QHttpNetworkConnection::setSslContext(std::shared_ptr<QSslContext> context)
{
Q_D(QHttpNetworkConnection);
d->sslContext = std::move(context);
@@ -1501,7 +1498,9 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel)
return;
if (channel == -1) { // ignore for all channels
- for (int i = 0; i < d->activeChannelCount; ++i) {
+ // We need to ignore for all channels, even the ones that are not in use just in case they
+ // will be in the future.
+ for (int i = 0; i < d->channelCount; ++i) {
d->channels[i].ignoreSslErrors();
}
@@ -1517,7 +1516,9 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int
return;
if (channel == -1) { // ignore for all channels
- for (int i = 0; i < d->activeChannelCount; ++i) {
+ // We need to ignore for all channels, even the ones that are not in use just in case they
+ // will be in the future.
+ for (int i = 0; i < d->channelCount; ++i) {
d->channels[i].ignoreSslErrors(errors);
}
@@ -1575,18 +1576,14 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN
// dialog is displaying
pauseConnection();
QHttpNetworkReply *reply;
- if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
- || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
-#if QT_CONFIG(ssl)
- || connectionType == QHttpNetworkConnection::ConnectionTypeSPDY
-#endif
- ) {
-
+ if ((connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
+ || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
// we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
// but that does not matter because the signal will ultimately be emitted
// by the QNetworkAccessManager.
- Q_ASSERT(chan->spdyRequestsToSend.count() > 0);
- reply = chan->spdyRequestsToSend.cbegin().value().second;
+ Q_ASSERT(chan->h2RequestsToSend.size() > 0);
+ reply = chan->h2RequestsToSend.cbegin().value().second;
} else { // HTTP
reply = chan->reply;
}
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 6808a0c0ac..5e4bce5eb0 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKCONNECTION_H
#define QHTTPNETWORKCONNECTION_H
@@ -55,7 +19,6 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
-#include <QtNetwork/qnetworksession.h>
#include <qhttp2configuration.h>
@@ -89,35 +52,21 @@ class QSslContext;
#endif // !QT_NO_SSL
class QHttpNetworkConnectionPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
+class Q_NETWORK_EXPORT QHttpNetworkConnection : public QObject
{
Q_OBJECT
public:
enum ConnectionType {
ConnectionTypeHTTP,
- ConnectionTypeSPDY,
ConnectionTypeHTTP2,
ConnectionTypeHTTP2Direct
};
-#ifndef QT_NO_BEARERMANAGEMENT
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
- ConnectionType connectionType = ConnectionTypeHTTP,
- QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession
- = QSharedPointer<QNetworkSession>());
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
- bool encrypt = false, QObject *parent = nullptr,
- QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>(),
+ bool encrypt = false, bool isLocalSocket = false,
+ QObject *parent = nullptr,
ConnectionType connectionType = ConnectionTypeHTTP);
-#else
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
- ConnectionType connectionType = ConnectionTypeHTTP,
- QObject *parent = 0);
- QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
- bool encrypt = false, QObject *parent = 0,
- ConnectionType connectionType = ConnectionTypeHTTP);
-#endif
~QHttpNetworkConnection();
//The hostname to which this is connected to.
@@ -141,7 +90,7 @@ public:
QHttpNetworkConnectionChannel *channels() const;
- ConnectionType connectionType();
+ ConnectionType connectionType() const;
void setConnectionType(ConnectionType type);
QHttp2Configuration http2Parameters() const;
@@ -151,8 +100,8 @@ public:
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
- QSharedPointer<QSslContext> sslContext();
- void setSslContext(QSharedPointer<QSslContext> context);
+ std::shared_ptr<QSslContext> sslContext() const;
+ void setSslContext(std::shared_ptr<QSslContext> context);
#endif
void preConnectFinished();
@@ -172,7 +121,6 @@ private:
friend class QHttpNetworkConnectionChannel;
friend class QHttp2ProtocolHandler;
friend class QHttpProtocolHandler;
- friend class QSpdyProtocolHandler;
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo))
@@ -187,8 +135,10 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
class QHttpNetworkConnectionPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QHttpNetworkConnection)
+ Q_DISABLE_COPY_MOVE(QHttpNetworkConnectionPrivate)
public:
- static const int defaultHttpChannelCount;
+ // Note: Only used from auto tests, normal usage is via QHttp1Configuration
+ static constexpr int defaultHttpChannelCount = 6;
static const int defaultPipelineLength;
static const int defaultRePipelineLength;
@@ -205,31 +155,31 @@ public:
IPv4or6
};
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type);
- QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
+ bool encrypt, bool isLocalSocket,
QHttpNetworkConnection::ConnectionType type);
~QHttpNetworkConnectionPrivate();
void init();
void pauseConnection();
void resumeConnection();
- ConnectionState state;
- NetworkLayerPreferenceState networkLayerState;
+ ConnectionState state = RunningState;
+ NetworkLayerPreferenceState networkLayerState = Unknown;
enum { ChunkSize = 4096 };
- int indexOf(QAbstractSocket *socket) const;
+ int indexOf(QIODevice *socket) const;
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
void fillHttp2Queue();
- bool dequeueRequest(QAbstractSocket *socket);
+ bool dequeueRequest(QIODevice *socket);
void prepareRequest(HttpMessagePair &request);
void updateChannel(int i, const HttpMessagePair &messagePair);
QHttpNetworkRequest predictNextRequest() const;
+ QHttpNetworkReply* predictNextRequestsReply() const;
- void fillPipeline(QAbstractSocket *socket);
+ void fillPipeline(QIODevice *socket);
bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
// read more HTTP body after the next event loop spin
@@ -247,9 +197,9 @@ public:
void _q_hostLookupFinished(const QHostInfo &info);
void _q_connectDelayedChannel();
- void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
+ void createAuthorization(QIODevice *socket, QHttpNetworkRequest &request);
- QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
+ QString errorDetail(QNetworkReply::NetworkError errorCode, QIODevice *socket,
const QString &extraDetail = QString());
void removeReply(QHttpNetworkReply *reply);
@@ -257,23 +207,30 @@ public:
QString hostName;
quint16 port;
bool encrypt;
- bool delayIpv4;
+ bool isLocalSocket;
+ bool delayIpv4 = true;
// Number of channels we are trying to use at the moment:
int activeChannelCount;
// The total number of channels we reserved:
const int channelCount;
QTimer delayedConnectionTimer;
- QHttpNetworkConnectionChannel *channels; // parallel connections to the server
- bool shouldEmitChannelError(QAbstractSocket *socket);
+ QHttpNetworkConnectionChannel * const channels; // parallel connections to the server
+ bool shouldEmitChannelError(QIODevice *socket);
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
- void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
- bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
- QUrl parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply);
+ void emitReplyError(QIODevice *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
+ bool handleAuthenticateChallenge(QIODevice *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+ struct ParseRedirectResult {
+ QUrl redirectUrl;
+ QNetworkReply::NetworkError errorCode;
+ };
+ static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
+ // Used by the HTTP1 code-path
+ QUrl parseRedirectResponse(QIODevice *socket, QHttpNetworkReply *reply);
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy networkProxy;
@@ -284,16 +241,12 @@ public:
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
- int preConnectRequests;
+ int preConnectRequests = 0;
QHttpNetworkConnection::ConnectionType connectionType;
#ifndef QT_NO_SSL
- QSharedPointer<QSslContext> sslContext;
-#endif
-
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession;
+ std::shared_ptr<QSslContext> sslContext;
#endif
QHttp2Configuration http2Parameters;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 47081b29d2..8688e4b8d7 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -1,55 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkconnectionchannel_p.h"
#include "qhttpnetworkconnection_p.h"
#include "qhttp2configuration.h"
#include "private/qnoncontiguousbytedevice_p.h"
-#include <qpair.h>
#include <qdebug.h>
#include <private/qhttp2protocolhandler_p.h>
#include <private/qhttpprotocolhandler_p.h>
-#include <private/qspdyprotocolhandler_p.h>
#include <private/http2protocol_p.h>
+#include <private/qsocketabstraction_p.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@@ -57,12 +20,11 @@
# include <QtNetwork/qsslcipher.h>
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
-#include "private/qnetworksession_p.h"
-#endif
-
#include "private/qnetconmonitor_p.h"
+#include <memory>
+#include <utility>
+
QT_BEGIN_NAMESPACE
namespace
@@ -79,7 +41,7 @@ private:
}
-// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
+// TODO: Put channel specific stuff here so it does not pollute qhttpnetworkconnection.cpp
// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
// connection times out)
@@ -98,8 +60,6 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
, lastStatus(0)
, pendingEncrypt(false)
, reconnectAttempts(reconnectAttemptsDefault)
- , authMethod(QAuthenticatorPrivate::None)
- , proxyAuthMethod(QAuthenticatorPrivate::None)
, authenticationCredentialsSent(false)
, proxyCredentialsSent(false)
, protocolHandler(nullptr)
@@ -119,70 +79,84 @@ void QHttpNetworkConnectionChannel::init()
#ifndef QT_NO_SSL
if (connection->d_func()->encrypt)
socket = new QSslSocket;
+ else if (connection->d_func()->isLocalSocket)
+ socket = new QLocalSocket;
else
socket = new QTcpSocket;
#else
socket = new QTcpSocket;
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- //push session down to socket
- if (networkSession)
- socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
-#endif
#ifndef QT_NO_NETWORKPROXY
// Set by QNAM anyway, but let's be safe here
- socket->setProxy(QNetworkProxy::NoProxy);
+ if (auto s = qobject_cast<QAbstractSocket *>(socket))
+ s->setProxy(QNetworkProxy::NoProxy);
#endif
// After some back and forth in all the last years, this is now a DirectConnection because otherwise
// the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
// which behave slightly differently on Windows vs Linux
- QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
- this, SLOT(_q_bytesWritten(qint64)),
+ QObject::connect(socket, &QIODevice::bytesWritten,
+ this, &QHttpNetworkConnectionChannel::_q_bytesWritten,
Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(connected()),
- this, SLOT(_q_connected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(readyRead()),
- this, SLOT(_q_readyRead()),
+ QObject::connect(socket, &QIODevice::readyRead,
+ this, &QHttpNetworkConnectionChannel::_q_readyRead,
Qt::DirectConnection);
- // The disconnected() and error() signals may already come
- // while calling connectToHost().
- // In case of a cached hostname or an IP this
- // will then emit a signal to the user of QNetworkReply
- // but cannot be caught because the user did not have a chance yet
- // to connect to QNetworkReply's signals.
- qRegisterMetaType<QAbstractSocket::SocketError>();
- QObject::connect(socket, SIGNAL(disconnected()),
- this, SLOT(_q_disconnected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(_q_error(QAbstractSocket::SocketError)),
- Qt::DirectConnection);
+
+ QSocketAbstraction::visit([this](auto *socket){
+ using SocketType = std::remove_pointer_t<decltype(socket)>;
+ QObject::connect(socket, &SocketType::connected,
+ this, &QHttpNetworkConnectionChannel::_q_connected,
+ Qt::DirectConnection);
+
+ // The disconnected() and error() signals may already come
+ // while calling connectToHost().
+ // In case of a cached hostname or an IP this
+ // will then emit a signal to the user of QNetworkReply
+ // but cannot be caught because the user did not have a chance yet
+ // to connect to QNetworkReply's signals.
+ QObject::connect(socket, &SocketType::disconnected,
+ this, &QHttpNetworkConnectionChannel::_q_disconnected,
+ Qt::DirectConnection);
+ if constexpr (std::is_same_v<SocketType, QAbstractSocket>) {
+ QObject::connect(socket, &QAbstractSocket::errorOccurred,
+ this, &QHttpNetworkConnectionChannel::_q_error,
+ Qt::DirectConnection);
+ } else if constexpr (std::is_same_v<SocketType, QLocalSocket>) {
+ auto convertAndForward = [this](QLocalSocket::LocalSocketError error) {
+ _q_error(static_cast<QAbstractSocket::SocketError>(error));
+ };
+ QObject::connect(socket, &SocketType::errorOccurred,
+ this, std::move(convertAndForward),
+ Qt::DirectConnection);
+ }
+ }, socket);
+
#ifndef QT_NO_NETWORKPROXY
- QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- Qt::DirectConnection);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
+ QObject::connect(s, &QAbstractSocket::proxyAuthenticationRequired,
+ this, &QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired,
+ Qt::DirectConnection);
+ }
#endif
#ifndef QT_NO_SSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
if (sslSocket) {
// won't be a sslSocket if encrypt is false
- QObject::connect(sslSocket, SIGNAL(encrypted()),
- this, SLOT(_q_encrypted()),
+ QObject::connect(sslSocket, &QSslSocket::encrypted,
+ this, &QHttpNetworkConnectionChannel::_q_encrypted,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
- this, SLOT(_q_sslErrors(QList<QSslError>)),
+ QObject::connect(sslSocket, &QSslSocket::sslErrors,
+ this, &QHttpNetworkConnectionChannel::_q_sslErrors,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
- this, SLOT(_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
+ QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired,
+ this, &QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
- this, SLOT(_q_encryptedBytesWritten(qint64)),
+ QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten,
+ this, &QHttpNetworkConnectionChannel::_q_encryptedBytesWritten,
Qt::DirectConnection);
if (ignoreAllSslErrors)
@@ -202,8 +176,10 @@ void QHttpNetworkConnectionChannel::init()
#endif
#ifndef QT_NO_NETWORKPROXY
- if (proxy.type() != QNetworkProxy::NoProxy)
- socket->setProxy(proxy);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket);
+ s && proxy.type() != QNetworkProxy::NoProxy) {
+ s->setProxy(proxy);
+ }
#endif
isInitialized = true;
}
@@ -216,7 +192,7 @@ void QHttpNetworkConnectionChannel::close()
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
@@ -236,7 +212,7 @@ void QHttpNetworkConnectionChannel::abort()
{
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
@@ -247,14 +223,17 @@ void QHttpNetworkConnectionChannel::abort()
if (socket) {
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
// there is no socket yet.
- socket->abort();
+ auto callAbort = [](auto *s) {
+ s->abort();
+ };
+ QSocketAbstraction::visit(callAbort, socket);
}
}
bool QHttpNetworkConnectionChannel::sendRequest()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
return protocolHandler->sendRequest();
}
@@ -267,7 +246,7 @@ bool QHttpNetworkConnectionChannel::sendRequest()
void QHttpNetworkConnectionChannel::sendRequestDelayed()
{
QMetaObject::invokeMethod(this, [this] {
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
if (reply)
protocolHandler->sendRequest();
}, Qt::ConnectionType::QueuedConnection);
@@ -275,13 +254,13 @@ void QHttpNetworkConnectionChannel::sendRequestDelayed()
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
protocolHandler->_q_receiveReply();
}
void QHttpNetworkConnectionChannel::_q_readyRead()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
protocolHandler->_q_readyRead();
}
@@ -314,7 +293,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (!isInitialized)
init();
- QAbstractSocket::SocketState socketState = socket->state();
+ QAbstractSocket::SocketState socketState = QSocketAbstraction::socketState(socket);
// resend this request after we receive the disconnected signal
// If !socket->isOpen() then we have already called close() on the socket, but there was still a
@@ -368,24 +347,43 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
QString connectHost = connection->d_func()->hostName;
quint16 connectPort = connection->d_func()->port;
+ QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
+ if (potentialReply) {
+ QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
+ } else if (!h2RequestsToSend.isEmpty()) {
+ QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
+ }
+
#ifndef QT_NO_NETWORKPROXY
// HTTPS always use transparent proxy.
if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
connectHost = connection->d_func()->networkProxy.hostName();
connectPort = connection->d_func()->networkProxy.port();
}
- if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
+ if (auto *abSocket = qobject_cast<QAbstractSocket *>(socket);
+ abSocket && abSocket->proxy().type() == QNetworkProxy::HttpProxy) {
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
QByteArray value;
- // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
- if (request.url().isEmpty())
- value = connection->d_func()->predictNextRequest().headerField("user-agent");
- else
+ // ensureConnection is called before any request has been assigned, but can also be
+ // called again if reconnecting
+ if (request.url().isEmpty()) {
+ if (connection->connectionType()
+ == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
+ || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && !h2RequestsToSend.isEmpty())) {
+ value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
+ } else {
+ value = connection->d_func()->predictNextRequest().headerField("user-agent");
+ }
+ } else {
value = request.headerField("user-agent");
+ }
if (!value.isEmpty()) {
- QNetworkProxy proxy(socket->proxy());
- proxy.setRawHeader("User-Agent", value); //detaches
- socket->setProxy(proxy);
+ QNetworkProxy proxy(abSocket->proxy());
+ auto h = proxy.headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::UserAgent, value);
+ proxy.setHeaders(std::move(h));
+ abSocket->setProxy(proxy);
}
}
#endif
@@ -396,8 +394,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// check whether we can re-use an existing SSL session
// (meaning another socket in this connection has already
// performed a full handshake)
- if (!connection->sslContext().isNull())
- QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext());
+ if (auto ctx = connection->sslContext())
+ QSslSocketPrivate::checkSettingSslContext(sslSocket, std::move(ctx));
sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName);
sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
@@ -408,7 +406,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
- socket->setReadBufferSize(64*1024);
+ sslSocket->setReadBufferSize(64*1024);
#else
// Need to dequeue the request so that we can emit the error.
if (!reply)
@@ -422,17 +420,24 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
&& connection->cacheProxy().type() == QNetworkProxy::NoProxy
&& connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
#endif
- socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
- // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
- socket->setReadBufferSize(1*1024);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
+ s->connectToHost(connectHost, connectPort,
+ QIODevice::ReadWrite | QIODevice::Unbuffered,
+ networkLayerPreference);
+ // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
+ s->setReadBufferSize(1 * 1024);
+ } else if (auto *s = qobject_cast<QLocalSocket *>(socket)) {
+ s->connectToServer(connectHost);
+ }
#ifndef QT_NO_NETWORKPROXY
} else {
- socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
-
+ auto *s = qobject_cast<QAbstractSocket *>(socket);
+ Q_ASSERT(s);
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
- socket->setReadBufferSize(64*1024);
+ s->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
+ s->setReadBufferSize(64 * 1024);
}
#endif
}
@@ -474,20 +479,23 @@ void QHttpNetworkConnectionChannel::allDone()
// trick with ProtocolHandlerDeleter, a QObject-derived class.
// These dances below just make it somewhat exception-safe.
// 1. Create a new owner:
- QAbstractProtocolHandler *oldHandler = protocolHandler.data();
- QScopedPointer<ProtocolHandlerDeleter> deleter(new ProtocolHandlerDeleter(oldHandler));
+ QAbstractProtocolHandler *oldHandler = protocolHandler.get();
+ auto deleter = std::make_unique<ProtocolHandlerDeleter>(oldHandler);
// 2. Retire the old one:
- protocolHandler.take();
+ Q_UNUSED(protocolHandler.release());
// 3. Call 'deleteLater':
deleter->deleteLater();
// 3. Give up the ownerthip:
- deleter.take();
+ Q_UNUSED(deleter.release());
connection->fillHttp2Queue();
protocolHandler.reset(new QHttp2ProtocolHandler(this));
- QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ // If we only had one request sent with H2 allowed, we may fail to send
+ // a client preface and SETTINGS, which is required by RFC 7540, 3.2.
+ QMetaObject::invokeMethod(h2c, "ensureClientPrefaceSent", Qt::QueuedConnection);
return;
} else {
// Ok, whatever happened, we do not try HTTP/2 anymore ...
@@ -532,7 +540,7 @@ void QHttpNetworkConnectionChannel::allDone()
// move next from pipeline to current request
if (!alreadyPipelinedRequests.isEmpty()) {
- if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
+ if (resendCurrent || connectionCloseEnabled || QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState) {
// move the pipelined ones back to the main queue
requeueCurrentlyPipelinedRequests();
close();
@@ -563,7 +571,7 @@ void QHttpNetworkConnectionChannel::allDone()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else if (alreadyPipelinedRequests.isEmpty()) {
if (connectionCloseEnabled)
- if (socket->state() != QAbstractSocket::UnconnectedState)
+ if (QSocketAbstraction::socketState(socket) != QAbstractSocket::UnconnectedState)
close();
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -577,11 +585,11 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport()
QByteArray serverHeaderField;
if (
// check for HTTP/1.1
- (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
+ (reply->majorVersion() == 1 && reply->minorVersion() == 1)
// check for not having connection close
&& (!reply->d_func()->isConnectionCloseEnabled())
// check if it is still connected
- && (socket->state() == QAbstractSocket::ConnectedState)
+ && (QSocketAbstraction::socketState(socket) == QAbstractSocket::ConnectedState)
// check for broken servers in server reply header
// this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
&& (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
@@ -600,7 +608,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport()
// called when the connection broke and we need to queue some pipelined requests again
void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
{
- for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
+ for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
alreadyPipelinedRequests.clear();
@@ -685,25 +693,27 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
//this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
return false;
}
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (!uploadByteDevice)
- return true;
-
- if (uploadByteDevice->reset()) {
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
+ || switchedToHttp2) {
+ // The else branch doesn't make any sense for HTTP/2, since 1 channel is multiplexed into
+ // many streams. And having one stream fail to reset upload data should not completely close
+ // the channel. Handled in the http2 protocol handler.
+ } else if (QNonContiguousByteDevice *uploadByteDevice = request.uploadByteDevice()) {
+ if (!uploadByteDevice->reset()) {
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
+ return false;
+ }
written = 0;
- return true;
- } else {
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
- return false;
}
+ return true;
}
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
{
- if (socket)
- socket->setProxy(networkProxy);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ s->setProxy(networkProxy);
proxy = networkProxy;
}
@@ -854,7 +864,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
state = QHttpNetworkConnectionChannel::IdleState;
- if (alreadyPipelinedRequests.length()) {
+ if (alreadyPipelinedRequests.size()) {
// If nothing was in a pipeline, no need in calling
// _q_startNextRequest (which it does):
requeueCurrentlyPipelinedRequests();
@@ -864,7 +874,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
}
-void QHttpNetworkConnectionChannel::_q_connected()
+void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket *absSocket)
{
// For the Happy Eyeballs we need to check if this is the first channel to connect.
if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
@@ -875,15 +885,20 @@ void QHttpNetworkConnectionChannel::_q_connected()
else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
else {
- if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
+ if (absSocket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
else
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
}
connection->d_func()->networkLayerDetected(networkLayerPreference);
+ if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else {
- if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
- || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
+ bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
+ if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
+ && (networkLayerPreference != QAbstractSocket::IPv4Protocol && !anyProtocol))
+ || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6)
+ && (networkLayerPreference != QAbstractSocket::IPv6Protocol && !anyProtocol))) {
close();
// This is the second connection so it has to be closed and we can schedule it for another request.
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -893,7 +908,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
}
// improve performance since we get the request sent by the kernel ASAP
- //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
+ //absSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
// We have this commented out now. It did not have the effect we wanted. If we want to
// do this properly, Qt has to combine multiple HTTP requests into one buffer
// and send this to the kernel in one syscall and then the kernel immediately sends
@@ -902,16 +917,16 @@ void QHttpNetworkConnectionChannel::_q_connected()
// the requests into one TCP packet.
// not sure yet if it helps, but it makes sense
- socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
+ absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
- if (QNetworkStatusMonitor::isEnabled()) {
+ if (QNetworkConnectionMonitor::isEnabled()) {
auto connectionPrivate = connection->d_func();
if (!connectionPrivate->connectionMonitor.isMonitoring()) {
// Now that we have a pair of addresses, we can start monitoring the
// connection status to handle its loss properly.
- if (connectionPrivate->connectionMonitor.setTargets(socket->localAddress(), socket->peerAddress()))
+ if (connectionPrivate->connectionMonitor.setTargets(absSocket->localAddress(), absSocket->peerAddress()))
connectionPrivate->connectionMonitor.startMonitoring();
}
}
@@ -920,18 +935,17 @@ void QHttpNetworkConnectionChannel::_q_connected()
//channels[i].reconnectAttempts = 2;
if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
#ifndef QT_NO_SSL
- if (connection->sslContext().isNull()) {
+ if (!connection->sslContext()) {
// this socket is making the 1st handshake for this connection,
// we need to set the SSL context so new sockets can reuse it
- QSharedPointer<QSslContext> socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(socket));
- if (!socketSslContext.isNull())
- connection->setSslContext(socketSslContext);
+ if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(absSocket)))
+ connection->setSslContext(std::move(socketSslContext));
}
#endif
} else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
state = QHttpNetworkConnectionChannel::IdleState;
protocolHandler.reset(new QHttp2ProtocolHandler(this));
- if (spdyRequestsToSend.count() > 0) {
+ if (h2RequestsToSend.size() > 0) {
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -946,7 +960,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
switchedToHttp2 = false;
if (!reply)
- connection->d_func()->dequeueRequest(socket);
+ connection->d_func()->dequeueRequest(absSocket);
if (reply) {
if (tryProtocolUpgrade) {
@@ -959,6 +973,22 @@ void QHttpNetworkConnectionChannel::_q_connected()
}
}
+void QHttpNetworkConnectionChannel::_q_connected_local_socket(QLocalSocket *localSocket)
+{
+ state = QHttpNetworkConnectionChannel::IdleState;
+ if (!reply) // No reply object, try to dequeue a request (which is paired with a reply):
+ connection->d_func()->dequeueRequest(localSocket);
+ if (reply)
+ sendRequest();
+}
+
+void QHttpNetworkConnectionChannel::_q_connected()
+{
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ _q_connected_abstract_socket(s);
+ else if (auto *s = qobject_cast<QLocalSocket *>(socket))
+ _q_connected_local_socket(s);
+}
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
{
@@ -972,6 +1002,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
break;
case QAbstractSocket::ConnectionRefusedError:
errorCode = QNetworkReply::ConnectionRefusedError;
+#ifndef QT_NO_NETWORKPROXY
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+#endif
break;
case QAbstractSocket::RemoteHostClosedError:
// This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
@@ -984,12 +1018,12 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
// we do not resend, but must report errors if any request is in progress (note, while
// not in its sendRequest(), protocol handler switches the channel to IdleState, thus
// this check is under this condition in 'if'):
- if (protocolHandler.data()) {
+ if (protocolHandler) {
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
- || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
- auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && switchedToHttp2)) {
+ auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
h2Handler->handleConnectionClosure();
- protocolHandler.reset();
}
}
return;
@@ -1053,6 +1087,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
errorCode = QNetworkReply::TimeoutError;
break;
+ case QAbstractSocket::ProxyConnectionRefusedError:
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+ break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
break;
@@ -1099,6 +1136,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
if (reply) {
reply->d_func()->errorString = errorString;
+ reply->d_func()->httpErrorCode = errorCode;
emit reply->finishedWithError(errorCode, errorString);
reply = nullptr;
if (protocolHandler)
@@ -1108,15 +1146,13 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|| !connection->d_func()->lowPriorityQueue.isEmpty());
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
-#ifndef QT_NO_SSL
- || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
-#endif
- ) {
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit error for all replies
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
+ currentReply->d_func()->errorString = errorString;
+ currentReply->d_func()->httpErrorCode = errorCode;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(errorCode, errorString);
}
@@ -1129,7 +1165,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
//signal emission triggered event loop
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
@@ -1142,13 +1178,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
-#ifndef QT_NO_SSL
- || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
-#endif
- ) {
- if (spdyRequestsToSend.count() > 0)
+ if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && (switchedToHttp2 || h2RequestsToSend.size() > 0))
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
+ if (h2RequestsToSend.size() > 0)
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
} else { // HTTP
// Need to dequeue the request before we can emit the error.
@@ -1171,9 +1204,9 @@ void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::Network
{
if (reply)
emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
}
@@ -1190,17 +1223,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
// after establishing a secure connection we immediately start sending
// HTTP/2 frames.
switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
- case QSslConfiguration::NextProtocolNegotiationNegotiated:
- case QSslConfiguration::NextProtocolNegotiationUnsupported: {
+ case QSslConfiguration::NextProtocolNegotiationNegotiated: {
QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
// fall through to create a QHttpProtocolHandler
- } else if (nextProtocol == QSslConfiguration::NextProtocolSpdy3_0) {
- protocolHandler.reset(new QSpdyProtocolHandler(this));
- connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeSPDY);
- // no need to re-queue requests, if SPDY was enabled on the request it
- // has gone to the SPDY queue already
- break;
} else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
switchedToHttp2 = true;
protocolHandler.reset(new QHttp2ProtocolHandler(this));
@@ -1213,40 +1239,33 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
}
}
Q_FALLTHROUGH();
+ case QSslConfiguration::NextProtocolNegotiationUnsupported: // No agreement, try HTTP/1(.1)
case QSslConfiguration::NextProtocolNegotiationNone: {
protocolHandler.reset(new QHttpProtocolHandler(this));
- if (!sslConfiguration.data()) {
- // Our own auto-tests bypass the normal initialization (done by
- // QHttpThreadDelegate), this means in the past we'd have here
- // the default constructed QSslConfiguration without any protocols
- // to negotiate. Let's create it now:
- sslConfiguration.reset(new QSslConfiguration);
- }
- QList<QByteArray> protocols = sslConfiguration->allowedNextProtocols();
+ QSslConfiguration newConfiguration = sslSocket->sslConfiguration();
+ QList<QByteArray> protocols = newConfiguration.allowedNextProtocols();
const int nProtocols = protocols.size();
// Clear the protocol that we failed to negotiate, so we do not try
// it again on other channels that our connection can create/open.
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
- else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY)
- protocols.removeAll(QSslConfiguration::NextProtocolSpdy3_0);
if (nProtocols > protocols.size()) {
- sslConfiguration->setAllowedNextProtocols(protocols);
+ newConfiguration.setAllowedNextProtocols(protocols);
const int channelCount = connection->d_func()->channelCount;
for (int i = 0; i < channelCount; ++i)
- connection->d_func()->channels[i].setSslConfiguration(*sslConfiguration);
+ connection->d_func()->channels[i].setSslConfiguration(newConfiguration);
}
connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
- // We use only one channel for SPDY or HTTP/2, but normally six for
+ // We use only one channel for HTTP/2, but normally six for
// HTTP/1.1 - let's restore this number to the reserved number of
// channels:
if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
- // re-queue requests from SPDY queue to HTTP queue, if any
- requeueSpdyRequests();
+ // re-queue requests from HTTP/2 queue to HTTP queue, if any
+ requeueHttp2Requests();
}
break;
}
@@ -1266,35 +1285,34 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
state = QHttpNetworkConnectionChannel::IdleState;
pendingEncrypt = false;
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
- connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- // we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
- if (spdyRequestsToSend.count() > 0) {
+ if (!h2RequestsToSend.isEmpty()) {
+ // Similar to HTTP/1.1 counterpart below:
+ const auto &pair = std::as_const(h2RequestsToSend).first();
+ emit pair.second->encrypted();
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
- QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
} else { // HTTP
if (!reply)
connection->d_func()->dequeueRequest(socket);
if (reply) {
- reply->setSpdyWasUsed(false);
+ reply->setHttp2WasUsed(false);
Q_ASSERT(reply->d_func()->connectionChannel == this);
emit reply->encrypted();
}
if (reply)
sendRequestDelayed();
}
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionChannel::requeueSpdyRequests()
+void QHttpNetworkConnectionChannel::requeueHttp2Requests()
{
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
- connection->d_func()->requeueRequest(spdyPairs.at(a));
- }
- spdyRequestsToSend.clear();
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy)
+ connection->d_func()->requeueRequest(httpMessagePair);
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1312,11 +1330,11 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
emit reply->sslErrors(errors);
}
#ifndef QT_NO_SSL
- else { // SPDY
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
+ else { // HTTP/2
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->sslErrors(errors);
}
@@ -1336,10 +1354,10 @@ void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPr
if (reply)
emit reply->preSharedKeyAuthenticationRequired(authenticator);
} else {
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index 270b3eb9ba..853b647ecc 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKCONNECTIONCHANNEL_H
#define QHTTPNETWORKCONNECTIONCHANNEL_H
@@ -55,6 +19,7 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qlocalsocket.h>
#include <private/qobject_p.h>
#include <qauthenticator.h>
@@ -76,8 +41,11 @@
# include <QtNetwork/qtcpsocket.h>
#endif
+#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
+#include <memory>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -104,7 +72,7 @@ public:
ClosingState = 16,
BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|ClosingState)
};
- QAbstractSocket *socket;
+ QIODevice *socket;
bool ssl;
bool isInitialized;
ChannelState state;
@@ -116,17 +84,12 @@ public:
int lastStatus; // last status received on this channel
bool pendingEncrypt; // for https (send after encrypted)
int reconnectAttempts; // maximum 2 reconnection attempts
- QAuthenticatorPrivate::Method authMethod;
- QAuthenticatorPrivate::Method proxyAuthMethod;
QAuthenticator authenticator;
QAuthenticator proxyAuthenticator;
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
- QScopedPointer<QAbstractProtocolHandler> protocolHandler;
- // SPDY or HTTP/2 requests; SPDY is TLS-only, but
- // HTTP/2 can be cleartext also, that's why it's
- // outside of QT_NO_SSL section. Sorted by priority:
- QMultiMap<int, HttpMessagePair> spdyRequestsToSend;
+ std::unique_ptr<QAbstractProtocolHandler> protocolHandler;
+ QMultiMap<int, HttpMessagePair> h2RequestsToSend;
bool switchedToHttp2 = false;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
@@ -135,13 +98,10 @@ public:
void ignoreSslErrors();
void ignoreSslErrors(const QList<QSslError> &errors);
void setSslConfiguration(const QSslConfiguration &config);
- void requeueSpdyRequests(); // when we wanted SPDY but got HTTP
+ void requeueHttp2Requests(); // when we wanted HTTP/2 but got HTTP/1.1
#endif
// to emit the signal for all in-flight replies:
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message);
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession;
-#endif
// HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining
enum PipeliningSupport {
@@ -197,6 +157,8 @@ public:
void _q_bytesWritten(qint64 bytes); // proceed sending
void _q_readyRead(); // pending data to read
void _q_disconnected(); // disconnected from host
+ void _q_connected_abstract_socket(QAbstractSocket *socket);
+ void _q_connected_local_socket(QLocalSocket *socket);
void _q_connected(); // start sending request
void _q_error(QAbstractSocket::SocketError); // error from socket
#ifndef QT_NO_NETWORKPROXY
diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp
index 8ad01174b4..7f9c94dc9c 100644
--- a/src/network/access/qhttpnetworkheader.cpp
+++ b/src/network/access/qhttpnetworkheader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkheader_p.h"
@@ -48,27 +12,12 @@ QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
{
}
-QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
- :QSharedData(other)
-{
- url = other.url;
- fields = other.fields;
-}
-
qint64 QHttpNetworkHeaderPrivate::contentLength() const
{
bool ok = false;
// We are not using the headerField() method here because servers might send us multiple content-length
// headers which is crap (see QTBUG-15311). Therefore just take the first content-length header field.
- QByteArray value;
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
- end = fields.constEnd();
- for ( ; it != end; ++it)
- if (it->first.compare("content-length", Qt::CaseInsensitive) == 0) {
- value = it->second;
- break;
- }
-
+ QByteArray value = parser.firstHeaderField("content-length");
qint64 length = value.toULongLong(&ok);
if (ok)
return length;
@@ -80,7 +29,7 @@ void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
setHeaderField("Content-Length", QByteArray::number(length));
}
-QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkHeaderPrivate::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
QList<QByteArray> allValues = headerFieldValues(name);
if (allValues.isEmpty())
@@ -89,32 +38,29 @@ QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const
return allValues.join(", ");
}
-QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
+QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(QByteArrayView name) const
{
- QList<QByteArray> result;
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
- end = fields.constEnd();
- for ( ; it != end; ++it)
- if (name.compare(it->first, Qt::CaseInsensitive) == 0)
- result += it->second;
-
- return result;
+ return parser.headerFieldValues(name);
}
void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
{
- auto firstEqualsName = [&name](const QPair<QByteArray, QByteArray> &header) {
- return name.compare(header.first, Qt::CaseInsensitive) == 0;
- };
- fields.erase(std::remove_if(fields.begin(), fields.end(),
- firstEqualsName),
- fields.end());
- fields.append(qMakePair(name, data));
+ parser.setHeaderField(name, data);
}
void QHttpNetworkHeaderPrivate::prependHeaderField(const QByteArray &name, const QByteArray &data)
{
- fields.prepend(qMakePair(name, data));
+ parser.prependHeaderField(name, data);
+}
+
+QHttpHeaders QHttpNetworkHeaderPrivate::headers() const
+{
+ return parser.headers();
+}
+
+void QHttpNetworkHeaderPrivate::clearHeaders()
+{
+ parser.clearHeaders();
}
bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
index 3adb0ed7f1..afbc6cb6fe 100644
--- a/src/network/access/qhttpnetworkheader_p.h
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKHEADER_H
#define QHTTPNETWORKHEADER_H
@@ -52,18 +16,22 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/private/qhttpheaderparser_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include <qshareddata.h>
#include <qurl.h>
+#ifndef Q_OS_WASM
QT_REQUIRE_CONFIG(http);
+#endif
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QHttpNetworkHeader
{
public:
- virtual ~QHttpNetworkHeader() {};
+ virtual ~QHttpNetworkHeader() {}
virtual QUrl url() const = 0;
virtual void setUrl(const QUrl &url) = 0;
@@ -73,8 +41,8 @@ public:
virtual qint64 contentLength() const = 0;
virtual void setContentLength(qint64 length) = 0;
- virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
- virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
+ virtual QHttpHeaders header() const = 0;
+ virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0;
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
};
@@ -82,17 +50,19 @@ class Q_AUTOTEST_EXPORT QHttpNetworkHeaderPrivate : public QSharedData
{
public:
QUrl url;
- QList<QPair<QByteArray, QByteArray> > fields;
+ QHttpHeaderParser parser;
QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
- QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
+ QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other) = default;
qint64 contentLength() const;
void setContentLength(qint64 length);
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
void setHeaderField(const QByteArray &name, const QByteArray &data);
void prependHeaderField(const QByteArray &name, const QByteArray &data);
+ void clearHeaders();
+ QHttpHeaders headers() const;
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
};
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index af456c3607..5711c96b18 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkreply_p.h"
#include "qhttpnetworkconnection_p.h"
@@ -46,12 +10,12 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
-#ifndef QT_NO_COMPRESS
-#include <zlib.h>
-#endif
+#include <private/qdecompresshelper_p.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
: QObject(*new QHttpNetworkReplyPrivate(url), parent)
{
@@ -63,11 +27,6 @@ QHttpNetworkReply::~QHttpNetworkReply()
if (d->connection) {
d->connection->d_func()->removeReply(this);
}
-
-#ifndef QT_NO_COMPRESS
- if (d->autoDecompress && d->isCompressed() && d->inflateStrm)
- inflateEnd(d->inflateStrm);
-#endif
}
QUrl QHttpNetworkReply::url() const
@@ -108,12 +67,12 @@ void QHttpNetworkReply::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
+QHttpHeaders QHttpNetworkReply::header() const
{
- return d_func()->fields;
+ return d_func()->parser.headers();
}
-QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkReply::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d_func()->headerField(name, defaultValue);
}
@@ -124,7 +83,13 @@ void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray
d->setHeaderField(name, data);
}
-void QHttpNetworkReply::parseHeader(const QByteArray &header)
+void QHttpNetworkReply::appendHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ Q_D(QHttpNetworkReply);
+ d->appendHeaderField(name, data);
+}
+
+void QHttpNetworkReply::parseHeader(QByteArrayView header)
{
Q_D(QHttpNetworkReply);
d->parseHeader(header);
@@ -144,13 +109,13 @@ void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
int QHttpNetworkReply::statusCode() const
{
- return d_func()->statusCode;
+ return d_func()->parser.getStatusCode();
}
void QHttpNetworkReply::setStatusCode(int code)
{
Q_D(QHttpNetworkReply);
- d->statusCode = code;
+ d->parser.setStatusCode(code);
}
QString QHttpNetworkReply::errorString() const
@@ -158,9 +123,19 @@ QString QHttpNetworkReply::errorString() const
return d_func()->errorString;
}
+QNetworkReply::NetworkError QHttpNetworkReply::errorCode() const
+{
+ return d_func()->httpErrorCode;
+}
+
QString QHttpNetworkReply::reasonPhrase() const
{
- return d_func()->reasonPhrase;
+ return d_func()->parser.getReasonPhrase();
+}
+
+void QHttpNetworkReply::setReasonPhrase(const QString &reason)
+{
+ d_func()->parser.setReasonPhrase(reason);
}
void QHttpNetworkReply::setErrorString(const QString &error)
@@ -171,12 +146,22 @@ void QHttpNetworkReply::setErrorString(const QString &error)
int QHttpNetworkReply::majorVersion() const
{
- return d_func()->majorVersion;
+ return d_func()->parser.getMajorVersion();
}
int QHttpNetworkReply::minorVersion() const
{
- return d_func()->minorVersion;
+ return d_func()->parser.getMinorVersion();
+}
+
+void QHttpNetworkReply::setMajorVersion(int version)
+{
+ d_func()->parser.setMajorVersion(version);
+}
+
+void QHttpNetworkReply::setMinorVersion(int version)
+{
+ d_func()->parser.setMinorVersion(version);
}
qint64 QHttpNetworkReply::bytesAvailable() const
@@ -250,7 +235,8 @@ void QHttpNetworkReply::setReadBufferSize(qint64 size)
bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
{
Q_D(QHttpNetworkReply);
- return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200);
+ return !d->isChunked() && !d->autoDecompress &&
+ d->bodyLength > 0 && d->parser.getStatusCode() == 200;
}
void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
@@ -287,14 +273,14 @@ bool QHttpNetworkReply::isPipeliningUsed() const
return d_func()->pipeliningUsed;
}
-bool QHttpNetworkReply::isSpdyUsed() const
+bool QHttpNetworkReply::isHttp2Used() const
{
- return d_func()->spdyUsed;
+ return d_func()->h2Used;
}
-void QHttpNetworkReply::setSpdyWasUsed(bool spdy)
+void QHttpNetworkReply::setHttp2WasUsed(bool h2)
{
- d_func()->spdyUsed = spdy;
+ d_func()->h2Used = h2;
}
qint64 QHttpNetworkReply::removedContentLength() const
@@ -316,48 +302,32 @@ QHttpNetworkConnection* QHttpNetworkReply::connection()
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl)
, state(NothingDoneState)
- , ssl(false)
- , statusCode(100),
- majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
+ , ssl(false),
+ bodyLength(0), contentRead(0), totalProgress(0),
chunkedTransferEncoding(false),
connectionCloseEnabled(true),
forceConnectionCloseEnabled(false),
lastChunkRead(false),
currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0),
- windowSizeDownload(65536), // 64K initial window size according to SPDY standard
- windowSizeUpload(65536), // 64K initial window size according to SPDY standard
- currentlyReceivedDataInWindow(0),
- currentlyUploadedDataInWindow(0),
totallyUploadedData(0),
removedContentLength(-1),
connection(nullptr),
autoDecompress(false), responseData(), requestIsPrepared(false)
- ,pipeliningUsed(false), spdyUsed(false), downstreamLimited(false)
+ ,pipeliningUsed(false), h2Used(false), downstreamLimited(false)
,userProvidedDownloadBuffer(nullptr)
-#ifndef QT_NO_COMPRESS
- ,inflateStrm(nullptr)
-#endif
{
QString scheme = newUrl.scheme();
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https"))
+ if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1)
// make sure we do not close the socket after preconnecting
connectionCloseEnabled = false;
}
-QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
-{
-#ifndef QT_NO_COMPRESS
- if (inflateStrm)
- delete inflateStrm;
-#endif
-}
+QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate() = default;
void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
{
state = NothingDoneState;
- statusCode = 100;
bodyLength = 0;
contentRead = 0;
totalProgress = 0;
@@ -365,11 +335,7 @@ void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
currentChunkRead = 0;
lastChunkRead = false;
connectionCloseEnabled = true;
-#ifndef QT_NO_COMPRESS
- if (autoDecompress && inflateStrm)
- inflateEnd(inflateStrm);
-#endif
- fields.clear();
+ parser.clear();
}
// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
@@ -387,72 +353,32 @@ qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
return (state != ReadingDataState ? 0 : fragment.size());
}
-bool QHttpNetworkReplyPrivate::isCompressed()
+bool QHttpNetworkReplyPrivate::isCompressed() const
{
- QByteArray encoding = headerField("content-encoding");
- return encoding.compare("gzip", Qt::CaseInsensitive) == 0 ||
- encoding.compare("deflate", Qt::CaseInsensitive) == 0;
+ return QDecompressHelper::isSupportedEncoding(headerField("content-encoding"));
}
-void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
+bool QHttpNetworkReply::isCompressed() const
{
- // The header "Content-Encoding = gzip" is retained.
- // Content-Length is removed since the actual one sent by the server is for compressed data
- QByteArray name("content-length");
- QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
- end = fields.end();
- while (it != end) {
- if (name.compare(it->first, Qt::CaseInsensitive) == 0) {
- removedContentLength = strtoull(it->second.constData(), nullptr, 0);
- fields.erase(it);
- break;
- }
- ++it;
- }
+ Q_D(const QHttpNetworkReply);
+ return d->isCompressed();
}
-bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
+void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
{
- challenge.clear();
- // find out the type of authentication protocol requested.
- QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
- // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i);
- // todo use qstrincmp
- if (!line.toLower().startsWith("negotiate"))
- challenge = line;
- }
- return !challenge.isEmpty();
-}
-
-QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
-{
- // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
- QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
- QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i).trimmed().toLower();
- if (method < QAuthenticatorPrivate::Basic
- && line.startsWith("basic")) {
- method = QAuthenticatorPrivate::Basic;
- } else if (method < QAuthenticatorPrivate::Ntlm
- && line.startsWith("ntlm")) {
- method = QAuthenticatorPrivate::Ntlm;
- } else if (method < QAuthenticatorPrivate::DigestMd5
- && line.startsWith("digest")) {
- method = QAuthenticatorPrivate::DigestMd5;
- } else if (method < QAuthenticatorPrivate::Negotiate
- && line.startsWith("negotiate")) {
- method = QAuthenticatorPrivate::Negotiate;
- }
+ // The header "Content-Encoding = gzip" is retained.
+ // Content-Length is removed since the actual one sent by the server is for compressed data
+ constexpr auto name = QByteArrayView("content-length");
+ QByteArray contentLength = parser.firstHeaderField(name);
+ bool parseOk = false;
+ qint64 value = contentLength.toLongLong(&parseOk);
+ if (parseOk) {
+ removedContentLength = value;
+ parser.removeHeaderField(name);
}
- return method;
}
-qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readStatus(QIODevice *socket)
{
if (fragment.isEmpty()) {
// reserve bytes for the status line. This is better than always append() which reallocs the byte array
@@ -478,7 +404,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
if (c == '\n') {
// remove the CR at the end
if (fragment.endsWith('\r')) {
- fragment.truncate(fragment.length()-1);
+ fragment.truncate(fragment.size()-1);
}
bool ok = parseStatus(fragment);
state = ReadingHeaderState;
@@ -492,7 +418,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
}
// is this a valid reply?
- if (fragment.length() == 5 && !fragment.startsWith("HTTP/")) {
+ if (fragment.size() == 5 && !fragment.startsWith("HTTP/")) {
fragment.clear();
return -1;
}
@@ -501,43 +427,12 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return bytes;
}
-bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
+bool QHttpNetworkReplyPrivate::parseStatus(QByteArrayView status)
{
- // from RFC 2616:
- // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
- // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
- // that makes: 'HTTP/n.n xxx Message'
- // byte count: 0123456789012
-
- static const int minLength = 11;
- static const int dotPos = 6;
- static const int spacePos = 8;
- static const char httpMagic[] = "HTTP/";
-
- if (status.length() < minLength
- || !status.startsWith(httpMagic)
- || status.at(dotPos) != '.'
- || status.at(spacePos) != ' ') {
- // I don't know how to parse this status line
- return false;
- }
-
- // optimize for the valid case: defer checking until the end
- majorVersion = status.at(dotPos - 1) - '0';
- minorVersion = status.at(dotPos + 1) - '0';
-
- int i = spacePos;
- int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
- const QByteArray code = status.mid(i + 1, j - i - 1);
-
- bool ok;
- statusCode = code.toInt(&ok);
- reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
-
- return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
+ return parser.parseStatus(status);
}
-qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readHeader(QIODevice *socket)
{
if (fragment.isEmpty()) {
// according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
@@ -571,8 +466,8 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
allHeaders = true;
// there is another case: We have no headers. Then the fragment equals just the line ending
- if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
- || (fragment.length() == 1 && fragment.endsWith("\n")))
+ if ((fragment.size() == 2 && fragment.endsWith("\r\n"))
+ || (fragment.size() == 1 && fragment.endsWith("\n")))
allHeaders = true;
}
}
@@ -593,54 +488,20 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
// check for explicit indication of close or the implicit connection close of HTTP/1.0
connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
headerField("proxy-connection").toLower().contains("close")) ||
- (majorVersion == 1 && minorVersion == 0 &&
+ (parser.getMajorVersion() == 1 && parser.getMinorVersion() == 0 &&
(connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
-
-#ifndef QT_NO_COMPRESS
- if (autoDecompress && isCompressed()) {
- // allocate inflate state
- if (!inflateStrm)
- inflateStrm = new z_stream;
- int ret = initializeInflateStream();
- if (ret != Z_OK)
- return -1;
- }
-#endif
-
}
return bytes;
}
-void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
+void QHttpNetworkReplyPrivate::parseHeader(QByteArrayView header)
{
- // see rfc2616, sec 4 for information about HTTP/1.1 headers.
- // allows relaxed parsing here, accepts both CRLF & LF line endings
- int i = 0;
- while (i < header.count()) {
- int j = header.indexOf(':', i); // field-name
- if (j == -1)
- break;
- const QByteArray field = header.mid(i, j - i).trimmed();
- j++;
- // any number of LWS is allowed before and after the value
- QByteArray value;
- do {
- i = header.indexOf('\n', j);
- if (i == -1)
- break;
- if (!value.isEmpty())
- value += ' ';
- // check if we have CRLF or only LF
- bool hasCR = (i && header[i-1] == '\r');
- int length = i -(hasCR ? 1: 0) - j;
- value += header.mid(j, length).trimmed();
- j = ++i;
- } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
- if (i == -1)
- break; // something is wrong
-
- fields.append(qMakePair(field, value));
- }
+ parser.parseHeaders(header);
+}
+
+void QHttpNetworkReplyPrivate::appendHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ parser.appendHeaderField(name, data);
}
bool QHttpNetworkReplyPrivate::isChunked()
@@ -655,7 +516,7 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QIODevice *socket, char *b)
{
// This first read is to flush the buffer inside the socket
qint64 haveRead = 0;
@@ -674,7 +535,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
+qint64 QHttpNetworkReplyPrivate::readBodyFast(QIODevice *socket, QByteDataBuffer *rb)
{
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
@@ -704,121 +565,27 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData
}
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readBody(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
-#ifndef QT_NO_COMPRESS
- // for gzip we'll allocate a temporary one that we then decompress
- QByteDataBuffer *tempOutDataBuffer = (autoDecompress ? new QByteDataBuffer : out);
-#else
- QByteDataBuffer *tempOutDataBuffer = out;
-#endif
-
-
if (isChunked()) {
// chunked transfer encoding (rfc 2616, sec 3.6)
- bytes += readReplyBodyChunked(socket, tempOutDataBuffer);
+ bytes += readReplyBodyChunked(socket, out);
} else if (bodyLength > 0) {
// we have a Content-Length
- bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead);
+ bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
if (contentRead + bytes == bodyLength)
state = AllDoneState;
} else {
// no content length. just read what's possible
- bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable());
- }
-
-#ifndef QT_NO_COMPRESS
- // This is true if there is compressed encoding and we're supposed to use it.
- if (autoDecompress) {
- qint64 uncompressRet = uncompressBodyData(tempOutDataBuffer, out);
- delete tempOutDataBuffer;
- if (uncompressRet < 0)
- return -1;
+ bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
}
-#endif
-
contentRead += bytes;
return bytes;
}
-#ifndef QT_NO_COMPRESS
-int QHttpNetworkReplyPrivate::initializeInflateStream()
-{
- Q_ASSERT(inflateStrm);
-
- inflateStrm->zalloc = Z_NULL;
- inflateStrm->zfree = Z_NULL;
- inflateStrm->opaque = Z_NULL;
- inflateStrm->avail_in = 0;
- inflateStrm->next_in = Z_NULL;
- // "windowBits can also be greater than 15 for optional gzip decoding.
- // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
- // http://www.zlib.net/manual.html
- int ret = inflateInit2(inflateStrm, MAX_WBITS+32);
- Q_ASSERT(ret == Z_OK);
- return ret;
-}
-
-qint64 QHttpNetworkReplyPrivate::uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out)
-{
- if (!inflateStrm) { // happens when called from the SPDY protocol handler
- inflateStrm = new z_stream;
- initializeInflateStream();
- }
-
- if (!inflateStrm)
- return -1;
-
- bool triedRawDeflate = false;
- for (int i = 0; i < in->bufferCount(); i++) {
- QByteArray &bIn = (*in)[i];
-
- inflateStrm->avail_in = bIn.size();
- inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
-
- do {
- QByteArray bOut;
- // make a wild guess about the uncompressed size.
- bOut.reserve(inflateStrm->avail_in * 3 + 512);
- inflateStrm->avail_out = bOut.capacity();
- inflateStrm->next_out = reinterpret_cast<Bytef*>(bOut.data());
-
- int ret = inflate(inflateStrm, Z_NO_FLUSH);
- //All negative return codes are errors, in the context of HTTP compression, Z_NEED_DICT is also an error.
- // in the case where we get Z_DATA_ERROR this could be because we received raw deflate compressed data.
- if (ret == Z_DATA_ERROR && !triedRawDeflate) {
- inflateEnd(inflateStrm);
- triedRawDeflate = true;
- inflateStrm->zalloc = Z_NULL;
- inflateStrm->zfree = Z_NULL;
- inflateStrm->opaque = Z_NULL;
- inflateStrm->avail_in = 0;
- inflateStrm->next_in = Z_NULL;
- int ret = inflateInit2(inflateStrm, -MAX_WBITS);
- if (ret != Z_OK) {
- return -1;
- } else {
- inflateStrm->avail_in = bIn.size();
- inflateStrm->next_in = reinterpret_cast<Bytef*>(bIn.data());
- continue;
- }
- } else if (ret < 0 || ret == Z_NEED_DICT) {
- return -1;
- }
- bOut.resize(bOut.capacity() - inflateStrm->avail_out);
- out->append(bOut);
- if (ret == Z_STREAM_END)
- return out->byteAmount();
- } while (inflateStrm->avail_in > 0);
- }
-
- return out->byteAmount();
-}
-#endif
-
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *socket, QByteDataBuffer *out, qint64 size)
{
// FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
@@ -851,7 +618,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByte
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
while (socket->bytesAvailable()) {
@@ -913,7 +680,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, Q
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
@@ -934,8 +701,8 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *c
bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
- fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
- *chunkSize = fragment.toLong(&ok, 16);
+ const auto fragmentView = QByteArrayView(fragment).mid(0, fragment.indexOf(';')).trimmed();
+ *chunkSize = fragmentView.toLong(&ok, 16);
fragment.clear();
break; // size done
} else {
@@ -957,7 +724,7 @@ bool QHttpNetworkReplyPrivate::isRedirecting() const
{
// We're in the process of redirecting - if the HTTP status code says so and
// followRedirect is switched on
- return (QHttpNetworkReply::isHttpRedirect(statusCode)
+ return (QHttpNetworkReply::isHttpRedirect(parser.getStatusCode())
&& request.isFollowRedirects());
}
@@ -965,11 +732,12 @@ bool QHttpNetworkReplyPrivate::shouldEmitSignals()
{
// for 401 & 407 don't emit the data signals. Content along with these
// responses are sent only if the authentication fails.
- return (statusCode != 401 && statusCode != 407);
+ return parser.getStatusCode() != 401 && parser.getStatusCode() != 407;
}
bool QHttpNetworkReplyPrivate::expectContent()
{
+ int statusCode = parser.getStatusCode();
// check whether we can expect content after the headers (rfc 2616, sec4.4)
if ((statusCode >= 100 && statusCode < 200)
|| statusCode == 204 || statusCode == 304)
@@ -989,7 +757,6 @@ bool QHttpNetworkReplyPrivate::expectContent()
void QHttpNetworkReplyPrivate::eraseData()
{
- compressedData.clear();
responseData.clear();
}
@@ -1037,3 +804,5 @@ void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
QT_END_NAMESPACE
+
+#include "moc_qhttpnetworkreply_p.cpp"
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 12cfe359aa..caec82bd7e 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKREPLY_H
#define QHTTPNETWORKREPLY_H
@@ -55,10 +19,6 @@
#include <qplatformdefs.h>
-#ifndef QT_NO_COMPRESS
-struct z_stream_s;
-#endif
-
#include <QtNetwork/qtcpsocket.h>
// it's safe to include these even if SSL support is not enabled
#include <QtNetwork/qsslsocket.h>
@@ -75,6 +35,16 @@ struct z_stream_s;
#include <private/qringbuffer_p.h>
#include <private/qbytedata_p.h>
+#ifndef QT_NO_NETWORKPROXY
+Q_MOC_INCLUDE(<QtNetwork/QNetworkProxy>)
+#endif
+Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
+
+#include <private/qdecompresshelper_p.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -84,7 +54,7 @@ class QHttpNetworkConnectionChannel;
class QHttpNetworkRequest;
class QHttpNetworkConnectionPrivate;
class QHttpNetworkReplyPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
+class Q_NETWORK_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
{
Q_OBJECT
public:
@@ -97,14 +67,17 @@ public:
int majorVersion() const override;
int minorVersion() const override;
+ void setMajorVersion(int version);
+ void setMinorVersion(int version);
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
- void parseHeader(const QByteArray &header); // mainly for testing
+ void appendHeaderField(const QByteArray &name, const QByteArray &data);
+ void parseHeader(QByteArrayView header); // used for testing
QHttpNetworkRequest request() const;
void setRequest(const QHttpNetworkRequest &request);
@@ -115,7 +88,10 @@ public:
QString errorString() const;
void setErrorString(const QString &error);
+ QNetworkReply::NetworkError errorCode() const;
+
QString reasonPhrase() const;
+ void setReasonPhrase(const QString &reason);
qint64 bytesAvailable() const;
qint64 bytesAvailableNextBlock() const;
@@ -137,8 +113,8 @@ public:
bool isFinished() const;
bool isPipeliningUsed() const;
- bool isSpdyUsed() const;
- void setSpdyWasUsed(bool spdy);
+ bool isHttp2Used() const;
+ void setHttp2WasUsed(bool h2Used);
qint64 removedContentLength() const;
bool isRedirecting() const;
@@ -150,6 +126,8 @@ public:
static bool isHttpRedirect(int statusCode);
+ bool isCompressed() const;
+
#ifndef QT_NO_SSL
QSslConfiguration sslConfiguration() const;
void setSslConfiguration(const QSslConfiguration &config);
@@ -163,6 +141,8 @@ Q_SIGNALS:
#endif
Q_SIGNALS:
+ void socketStartedConnecting();
+ void requestSent();
void readyRead();
void finished();
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
@@ -192,21 +172,20 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReplyPrivate : public QObjectPrivate, public
public:
QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
~QHttpNetworkReplyPrivate();
- qint64 readStatus(QAbstractSocket *socket);
- bool parseStatus(const QByteArray &status);
- qint64 readHeader(QAbstractSocket *socket);
- void parseHeader(const QByteArray &header);
- qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
- qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
- qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
- bool findChallenge(bool forProxy, QByteArray &challenge) const;
- QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
+ qint64 readStatus(QIODevice *socket);
+ bool parseStatus(QByteArrayView status);
+ qint64 readHeader(QIODevice *socket);
+ void parseHeader(QByteArrayView header);
+ void appendHeaderField(const QByteArray &name, const QByteArray &data);
+ qint64 readBody(QIODevice *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QIODevice *socket, char *b);
+ qint64 readBodyFast(QIODevice *socket, QByteDataBuffer *rb);
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
- qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
bool isRedirecting() const;
bool shouldEmitSignals();
@@ -217,7 +196,7 @@ public:
bool isChunked();
bool isConnectionCloseEnabled();
- bool isCompressed();
+ bool isCompressed() const;
void removeAutoDecompressHeader();
enum ReplyState {
@@ -235,11 +214,7 @@ public:
QHttpNetworkRequest request;
bool ssl;
- int statusCode;
- int majorVersion;
- int minorVersion;
QString errorString;
- QString reasonPhrase;
qint64 bodyLength;
qint64 contentRead;
qint64 totalProgress;
@@ -251,33 +226,23 @@ public:
qint64 currentChunkSize;
qint64 currentChunkRead;
qint64 readBufferMaxSize;
- qint32 windowSizeDownload; // only for SPDY
- qint32 windowSizeUpload; // only for SPDY
- qint32 currentlyReceivedDataInWindow; // only for SPDY
- qint32 currentlyUploadedDataInWindow; // only for SPDY
- qint64 totallyUploadedData; // only for SPDY
+ qint64 totallyUploadedData; // HTTP/2
qint64 removedContentLength;
QPointer<QHttpNetworkConnection> connection;
QPointer<QHttpNetworkConnectionChannel> connectionChannel;
+ QNetworkReply::NetworkError httpErrorCode = QNetworkReply::NoError;
bool autoDecompress;
QByteDataBuffer responseData; // uncompressed body
- QByteArray compressedData; // compressed body (temporary)
bool requestIsPrepared;
bool pipeliningUsed;
- bool spdyUsed;
+ bool h2Used;
bool downstreamLimited;
char* userProvidedDownloadBuffer;
QUrl redirectUrl;
-
-#ifndef QT_NO_COMPRESS
- z_stream_s *inflateStrm;
- int initializeInflateStream();
- qint64 uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out);
-#endif
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 5fb8885bdf..06cc0b4464 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -1,51 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkrequest_p.h"
#include "private/qnoncontiguousbytedevice_p.h"
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QHttpNetworkRequest)
+
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(nullptr),
- autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false),
+ autoDecompress(false), pipeliningAllowed(false), http2Allowed(true),
http2Direct(false), withCredentials(true), preConnect(false), redirectCount(0),
redirectPolicy(QNetworkRequest::ManualRedirectPolicy)
{
@@ -59,12 +25,13 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
uploadByteDevice(other.uploadByteDevice),
autoDecompress(other.autoDecompress),
pipeliningAllowed(other.pipeliningAllowed),
- spdyAllowed(other.spdyAllowed),
http2Allowed(other.http2Allowed),
http2Direct(other.http2Direct),
+ h2cAllowed(other.h2cAllowed),
withCredentials(other.withCredentials),
ssl(other.ssl),
preConnect(other.preConnect),
+ needResendWithCredentials(other.needResendWithCredentials),
redirectCount(other.redirectCount),
redirectPolicy(other.redirectPolicy),
peerVerifyName(other.peerVerifyName)
@@ -83,16 +50,18 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (uploadByteDevice == other.uploadByteDevice)
&& (autoDecompress == other.autoDecompress)
&& (pipeliningAllowed == other.pipeliningAllowed)
- && (spdyAllowed == other.spdyAllowed)
&& (http2Allowed == other.http2Allowed)
&& (http2Direct == other.http2Direct)
+ && (h2cAllowed == other.h2cAllowed)
// we do not clear the customVerb in setOperation
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
&& (withCredentials == other.withCredentials)
&& (ssl == other.ssl)
&& (preConnect == other.preConnect)
&& (redirectPolicy == other.redirectPolicy)
- && (peerVerifyName == other.peerVerifyName);
+ && (peerVerifyName == other.peerVerifyName)
+ && (needResendWithCredentials == other.needResendWithCredentials)
+ ;
}
QByteArray QHttpNetworkRequest::methodName() const
@@ -143,9 +112,9 @@ QByteArray QHttpNetworkRequest::uri(bool throughProxy) const
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
{
- QList<QPair<QByteArray, QByteArray> > fields = request.header();
+ const QHttpHeaders headers = request.header();
QByteArray ba;
- ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
+ ba.reserve(40 + headers.size() * 25); // very rough lower bound estimation
ba += request.methodName();
ba += ' ';
@@ -157,12 +126,10 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
ba += QByteArray::number(request.minorVersion());
ba += "\r\n";
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd();
- for (; it != endIt; ++it) {
- ba += it->first;
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ ba += headers.nameAt(i);
ba += ": ";
- ba += it->second;
+ ba += headers.valueAt(i);
ba += "\r\n";
}
if (request.d->operation == QHttpNetworkRequest::Post) {
@@ -268,12 +235,12 @@ void QHttpNetworkRequest::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
+QHttpHeaders QHttpNetworkRequest::header() const
{
- return d->fields;
+ return d->parser.headers();
}
-QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkRequest::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d->headerField(name, defaultValue);
}
@@ -288,6 +255,11 @@ void QHttpNetworkRequest::prependHeaderField(const QByteArray &name, const QByte
d->prependHeaderField(name, data);
}
+void QHttpNetworkRequest::clearHeaders()
+{
+ d->clearHeaders();
+}
+
QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
{
d = other.d;
@@ -339,16 +311,6 @@ void QHttpNetworkRequest::setPipeliningAllowed(bool b)
d->pipeliningAllowed = b;
}
-bool QHttpNetworkRequest::isSPDYAllowed() const
-{
- return d->spdyAllowed;
-}
-
-void QHttpNetworkRequest::setSPDYAllowed(bool b)
-{
- d->spdyAllowed = b;
-}
-
bool QHttpNetworkRequest::isHTTP2Allowed() const
{
return d->http2Allowed;
@@ -369,6 +331,16 @@ void QHttpNetworkRequest::setHTTP2Direct(bool b)
d->http2Direct = b;
}
+bool QHttpNetworkRequest::isH2cAllowed() const
+{
+ return d->h2cAllowed;
+}
+
+void QHttpNetworkRequest::setH2cAllowed(bool b)
+{
+ d->h2cAllowed = b;
+}
+
bool QHttpNetworkRequest::withCredentials() const
{
return d->withCredentials;
@@ -409,5 +381,15 @@ void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
+QString QHttpNetworkRequest::fullLocalServerName() const
+{
+ return d->fullLocalServerName;
+}
+
+void QHttpNetworkRequest::setFullLocalServerName(const QString &fullServerName)
+{
+ d->fullLocalServerName = fullServerName;
+}
+
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index fb4896195b..4444020402 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKREQUEST_H
#define QHTTPNETWORKREQUEST_H
@@ -56,7 +20,9 @@
#include <QtNetwork/qnetworkrequest.h>
#include <qmetatype.h>
+#ifndef Q_OS_WASM
QT_REQUIRE_CONFIG(http);
+#endif
QT_BEGIN_NAMESPACE
@@ -99,10 +65,11 @@ public:
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
void prependHeaderField(const QByteArray &name, const QByteArray &data);
+ void clearHeaders();
Operation operation() const;
void setOperation(Operation operation);
@@ -116,15 +83,15 @@ public:
bool isPipeliningAllowed() const;
void setPipeliningAllowed(bool b);
- bool isSPDYAllowed() const;
- void setSPDYAllowed(bool b);
-
bool isHTTP2Allowed() const;
void setHTTP2Allowed(bool b);
bool isHTTP2Direct() const;
void setHTTP2Direct(bool b);
+ bool isH2cAllowed() const;
+ void setH2cAllowed(bool b);
+
bool withCredentials() const;
void setWithCredentials(bool b);
@@ -149,6 +116,10 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
+
+ QString fullLocalServerName() const;
+ void setFullLocalServerName(const QString &fullServerName);
+
private:
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
friend class QHttpNetworkRequestPrivate;
@@ -172,16 +143,18 @@ public:
QHttpNetworkRequest::Operation operation;
QByteArray customVerb;
+ QString fullLocalServerName; // for local sockets
QHttpNetworkRequest::Priority priority;
mutable QNonContiguousByteDevice* uploadByteDevice;
bool autoDecompress;
bool pipeliningAllowed;
- bool spdyAllowed;
bool http2Allowed;
bool http2Direct;
+ bool h2cAllowed = false;
bool withCredentials;
bool ssl;
bool preConnect;
+ bool needResendWithCredentials = false;
int redirectCount;
QNetworkRequest::RedirectPolicy redirectPolicy;
QString peerVerifyName;
@@ -190,6 +163,6 @@ public:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QHttpNetworkRequest)
+QT_DECL_METATYPE_EXTERN(QHttpNetworkRequest, Q_AUTOTEST_EXPORT)
#endif // QHTTPNETWORKREQUEST_H
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index d39589fb96..fb584eb9cc 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -1,49 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qhttpprotocolhandler_p.h>
#include <private/qnoncontiguousbytedevice_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qsocketabstraction_p.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel)
{
@@ -68,10 +35,8 @@ void QHttpProtocolHandler::_q_receiveReply()
return;
}
- QAbstractSocket::SocketState socketState = m_socket->state();
-
// connection might be closed to signal the end of data
- if (socketState == QAbstractSocket::UnconnectedState) {
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::UnconnectedState) {
if (m_socket->bytesAvailable() <= 0) {
if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
// finish this reply. this case happens when the server did not send a content length
@@ -107,7 +72,7 @@ void QHttpProtocolHandler::_q_receiveReply()
return;
}
bytes += statusBytes;
- m_channel->lastStatus = m_reply->d_func()->statusCode;
+ m_channel->lastStatus = m_reply->statusCode();
break;
}
case QHttpNetworkReplyPrivate::ReadingHeaderState: {
@@ -127,7 +92,8 @@ void QHttpProtocolHandler::_q_receiveReply()
} else {
replyPrivate->autoDecompress = false;
}
- if (replyPrivate->statusCode == 100) {
+ const int statusCode = m_reply->statusCode();
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
replyPrivate->clearHttpLayerInformation();
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
break; // ignore
@@ -148,7 +114,7 @@ void QHttpProtocolHandler::_q_receiveReply()
}
case QHttpNetworkReplyPrivate::ReadingDataState: {
QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
- if (m_socket->state() == QAbstractSocket::ConnectedState &&
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::ConnectedState &&
replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
// (only do the following when still connected, not when we have already been disconnected and there is still data)
// We already have some HTTP body data. We don't read more from the socket until
@@ -177,8 +143,7 @@ void QHttpProtocolHandler::_q_receiveReply()
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::RemoteHostClosedError);
break;
}
- } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
+ } else if (!replyPrivate->isChunked() && replyPrivate->bodyLength > 0) {
// bulk files like images should fulfill these properties and
// we can therefore save on memory copying
qint64 haveRead = replyPrivate->readBodyFast(m_socket, &replyPrivate->responseData);
@@ -216,6 +181,8 @@ void QHttpProtocolHandler::_q_receiveReply()
}
case QHttpNetworkReplyPrivate::AllDoneState:
m_channel->allDone();
+ if (state == QHttpNetworkReplyPrivate::AllDoneState)
+ lastBytes = bytes; // No need to loop more just to call m_channel->allDone again.
break;
default:
break;
@@ -225,7 +192,8 @@ void QHttpProtocolHandler::_q_receiveReply()
void QHttpProtocolHandler::_q_readyRead()
{
- if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::ConnectedState
+ && m_socket->bytesAvailable() == 0) {
// We got a readyRead but no bytes are available..
// This happens for the Unbuffered QTcpSocket
// Also check if socket is in ConnectedState since
@@ -233,7 +201,7 @@ void QHttpProtocolHandler::_q_readyRead()
char c;
qint64 ret = m_socket->peek(&c, 1);
if (ret < 0) {
- m_channel->_q_error(m_socket->error());
+ m_channel->_q_error(QSocketAbstraction::socketError(m_socket));
// We still need to handle the reply so it emits its signals etc.
if (m_reply)
_q_receiveReply();
@@ -271,9 +239,7 @@ bool QHttpProtocolHandler::sendRequest()
// _q_connected or _q_encrypted
return false;
}
- QString scheme = m_channel->request.url().scheme();
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https")) {
+ if (m_channel->request.isPreConnect()) {
m_channel->state = QHttpNetworkConnectionChannel::IdleState;
m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
m_channel->allDone();
@@ -313,12 +279,12 @@ bool QHttpProtocolHandler::sendRequest()
if (m_channel->request.withCredentials())
m_connection->d_func()->createAuthorization(m_socket, m_channel->request);
#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request,
+ m_header = QHttpNetworkRequestPrivate::header(m_channel->request,
(m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
#else
- QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
+ m_header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
#endif
- m_socket->write(header);
+
// flushing is dangerous (QSslSocket calls transmit which might read or error)
// m_socket->flush();
QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
@@ -331,6 +297,9 @@ bool QHttpProtocolHandler::sendRequest()
m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
sendRequest(); //recurse
} else {
+ // no data to send: just send the HTTP headers
+ m_socket->write(std::exchange(m_header, {}));
+ QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
sendRequest(); //recurse
}
@@ -342,6 +311,10 @@ bool QHttpProtocolHandler::sendRequest()
// write the data
QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
+ // the upload device might have no data to send, but we still have to send the headers,
+ // do it now.
+ if (!m_header.isEmpty())
+ m_socket->write(std::exchange(m_header, {}));
if (uploadByteDevice)
emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
@@ -349,24 +322,31 @@ bool QHttpProtocolHandler::sendRequest()
break;
}
- // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it;
+ // note that the headers do not count towards these limits.
const qint64 socketBufferFill = 32*1024;
const qint64 socketWriteMaxSize = 16*1024;
-
+ // if it is really an ssl socket, check more than just bytesToWrite()
#ifndef QT_NO_SSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(m_socket);
- // if it is really an ssl socket, check more than just bytesToWrite()
- while ((m_socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
- <= socketBufferFill && m_channel->bytesTotal != m_channel->written)
+ const auto encryptedBytesToWrite = [sslSocket]() -> qint64
+ {
+ return sslSocket ? sslSocket->encryptedBytesToWrite() : 0;
+ };
#else
- while (m_socket->bytesToWrite() <= socketBufferFill
- && m_channel->bytesTotal != m_channel->written)
+ const auto encryptedBytesToWrite = [](){ return qint64(0); };
#endif
+
+ // throughout this loop, we want to send the data coming from uploadByteDevice.
+ // we also need to send the headers, as we try to coalesce their write with the data.
+ // we won't send more than the limits above, excluding the headers
+ while ((m_socket->bytesToWrite() + encryptedBytesToWrite()) <= socketBufferFill
+ && m_channel->bytesTotal != m_channel->written)
{
// get pointer to upload data
qint64 currentReadSize = 0;
- qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
+ const qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
if (currentReadSize == -1) {
@@ -384,7 +364,18 @@ bool QHttpProtocolHandler::sendRequest()
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
return false;
}
- qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ qint64 currentWriteSize;
+ if (m_header.isEmpty()) {
+ currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ } else {
+ // assemble header and data and send them together
+ const qint64 headerSize = m_header.size();
+ m_header.append(readPointer, currentReadSize);
+ currentWriteSize = m_socket->write(std::exchange(m_header, {}));
+ if (currentWriteSize != -1)
+ currentWriteSize -= headerSize;
+ QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
+ }
if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
// socket broke down
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
diff --git a/src/network/access/qhttpprotocolhandler_p.h b/src/network/access/qhttpprotocolhandler_p.h
index 8e766604bb..221e98d538 100644
--- a/src/network/access/qhttpprotocolhandler_p.h
+++ b/src/network/access/qhttpprotocolhandler_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPPROTOCOLHANDLER_H
#define QHTTPPROTOCOLHANDLER_H
@@ -55,6 +19,8 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qabstractprotocolhandler_p.h>
+#include <QtCore/qbytearray.h>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -67,6 +33,8 @@ private:
virtual void _q_receiveReply() override;
virtual void _q_readyRead() override;
virtual bool sendRequest() override;
+
+ QByteArray m_header;
};
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index f3125a3a95..4e5cf05aef 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QHTTPTHREADDELEGATE_DEBUG
#include "qhttpthreaddelegate_p.h"
@@ -52,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
{
QNetworkReply::NetworkError code;
@@ -128,14 +94,14 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
QString result;
QUrl copy = url;
QString scheme = copy.scheme();
- bool isEncrypted = scheme == QLatin1String("https")
- || scheme == QLatin1String("preconnect-https");
- copy.setPort(copy.port(isEncrypted ? 443 : 80));
- if (scheme == QLatin1String("preconnect-http")) {
- copy.setScheme(QLatin1String("http"));
- } else if (scheme == QLatin1String("preconnect-https")) {
- copy.setScheme(QLatin1String("https"));
- }
+ bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1;
+ const bool isLocalSocket = scheme.startsWith("unix"_L1);
+ if (!isLocalSocket)
+ copy.setPort(copy.port(isEncrypted ? 443 : 80));
+ if (scheme == "preconnect-http"_L1)
+ copy.setScheme("http"_L1);
+ else if (scheme == "preconnect-https"_L1)
+ copy.setScheme("https"_L1);
result = copy.toString(QUrl::RemoveUserInfo | QUrl::RemovePath |
QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::FullyEncoded);
@@ -145,12 +111,12 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
switch (proxy->type()) {
case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
+ key.setScheme("proxy-socks5"_L1);
break;
case QNetworkProxy::HttpProxy:
case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
+ key.setScheme("proxy-http"_L1);
break;
default:
@@ -169,10 +135,10 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
}
}
#else
- Q_UNUSED(proxy)
+ Q_UNUSED(proxy);
#endif
if (!peerVerifyName.isEmpty())
- result += QLatin1Char(':') + peerVerifyName;
+ result += u':' + peerVerifyName;
return "http-connection:" + std::move(result).toLatin1();
}
@@ -181,17 +147,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
{
// Q_OBJECT
public:
-#ifdef QT_NO_BEARERMANAGEMENT
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket,
QHttpNetworkConnection::ConnectionType connectionType)
- : QHttpNetworkConnection(hostName, port, encrypt, connectionType)
-#else
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType connectionType,
- QSharedPointer<QNetworkSession> networkSession)
- : QHttpNetworkConnection(hostName, port, encrypt, connectionType, /*parent=*/nullptr,
- std::move(networkSession))
-#endif
+ : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, isLocalSocket, /*parent=*/nullptr, connectionType)
{
setExpires(true);
setShareable(true);
@@ -234,9 +192,10 @@ QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
, pendingDownloadData()
, pendingDownloadProgress()
, synchronous(false)
+ , connectionCacheExpiryTimeoutSeconds(-1)
, incomingStatusCode(0)
, isPipeliningUsed(false)
- , isSpdyUsed(false)
+ , isHttp2Used(false)
, incomingContentLength(-1)
, removedContentLength(-1)
, incomingErrorCode(QNetworkReply::NoError)
@@ -265,7 +224,7 @@ void QHttpThreadDelegate::startRequestSynchronously()
synchronousRequestLoop.exec();
connections.localData()->releaseEntry(cacheKey);
- connections.setLocalData(0);
+ connections.setLocalData(nullptr);
#ifdef QHTTPTHREADDELEGATE_DEBUG
qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
@@ -287,7 +246,9 @@ void QHttpThreadDelegate::startRequest()
// check if we have an open connection to this host
QUrl urlCopy = httpRequest.url();
- urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
+ const bool isLocalSocket = urlCopy.scheme().startsWith("unix"_L1);
+ if (!isLocalSocket)
+ urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
QHttpNetworkConnection::ConnectionType connectionType
= httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
@@ -297,6 +258,12 @@ void QHttpThreadDelegate::startRequest()
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2Direct;
}
+ // Use HTTP/1.1 if h2c is not allowed and we would otherwise choose to use it
+ if (!ssl && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && !httpRequest.isH2cAllowed()) {
+ connectionType = QHttpNetworkConnection::ConnectionTypeHTTP;
+ }
+
#if QT_CONFIG(ssl)
// See qnetworkreplyhttpimpl, delegate's initialization code.
Q_ASSERT(!ssl || incomingSslConfiguration.data());
@@ -316,20 +283,18 @@ void QHttpThreadDelegate::startRequest()
} else
#endif // QT_CONFIG(ssl)
{
- urlCopy.setScheme(QStringLiteral("h2"));
+ if (isLocalSocket)
+ urlCopy.setScheme(QStringLiteral("unix+h2"));
+ else
+ urlCopy.setScheme(QStringLiteral("h2"));
}
}
-#ifndef QT_NO_SSL
- if (!isH2 && httpRequest.isSPDYAllowed() && ssl) {
- connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
- urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
- QList<QByteArray> nextProtocols;
- nextProtocols << QSslConfiguration::NextProtocolSpdy3_0
- << QSslConfiguration::NextProtocolHttp1_1;
- incomingSslConfiguration->setAllowedNextProtocols(nextProtocols);
+ QString extraData = httpRequest.peerVerifyName();
+ if (isLocalSocket) {
+ if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
+ extraData = path;
}
-#endif // QT_NO_SSL
#ifndef QT_NO_NETWORKPROXY
if (transparentProxy.type() != QNetworkProxy::NoProxy)
@@ -343,16 +308,19 @@ void QHttpThreadDelegate::startRequest()
// the http object is actually a QHttpNetworkConnection
httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
if (!httpConnection) {
+
+ QString host = urlCopy.host();
+ // Update the host if a unix socket path or named pipe is used:
+ if (isLocalSocket) {
+ if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
+ host = path;
+ }
+
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
-#ifdef QT_NO_BEARERMANAGEMENT
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
- connectionType);
-#else
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
- connectionType,
- networkSession);
-#endif // QT_NO_BEARERMANAGEMENT
+ httpConnection = new QNetworkAccessCachedHttpConnection(
+ http1Parameters.numberOfConnectionsPerHost(), host, urlCopy.port(), ssl,
+ isLocalSocket, connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
httpConnection->setHttp2Parameters(http2Parameters);
@@ -369,7 +337,7 @@ void QHttpThreadDelegate::startRequest()
#endif
httpConnection->setPeerVerifyName(httpRequest.peerVerifyName());
// cache the QHttpNetworkConnection corresponding to this cache key
- connections.localData()->addEntry(cacheKey, httpConnection);
+ connections.localData()->addEntry(cacheKey, httpConnection, connectionCacheExpiryTimeoutSeconds);
} else {
if (httpRequest.withCredentials()) {
QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), nullptr);
@@ -402,6 +370,8 @@ void QHttpThreadDelegate::startRequest()
// Don't care about ignored SSL errors for now in the synchronous HTTP case.
} else if (!synchronous) {
+ connect(httpReply,SIGNAL(socketStartedConnecting()), this, SIGNAL(socketStartedConnecting()));
+ connect(httpReply,SIGNAL(requestSent()), this, SIGNAL(requestSent()));
connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
@@ -428,6 +398,12 @@ void QHttpThreadDelegate::startRequest()
connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
+ if (httpReply->errorCode() != QNetworkReply::NoError) {
+ if (synchronous)
+ synchronousFinishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
+ else
+ finishedWithErrorSlot(httpReply->errorCode(), httpReply->errorString());
+ }
}
// This gets called from the user thread or by the synchronous HTTP timeout timer
@@ -532,8 +508,8 @@ void QHttpThreadDelegate::finishedSlot()
if (httpReply->statusCode() >= 400) {
// it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error transferring %1 - server replied: %2"));
+ QString msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error transferring %1 - server replied: %2"));
msg = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
}
@@ -558,12 +534,13 @@ void QHttpThreadDelegate::synchronousFinishedSlot()
#endif
if (httpReply->statusCode() >= 400) {
// it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error transferring %1 - server replied: %2"));
+ QString msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error transferring %1 - server replied: %2"));
incomingErrorDetail = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
}
+ isCompressed = httpReply->isCompressed();
synchronousDownloadData = httpReply->readAll();
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
@@ -612,11 +589,6 @@ void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::Networ
httpReply = nullptr;
}
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
void QHttpThreadDelegate::headerChangedSlot()
{
if (!httpReply)
@@ -634,14 +606,11 @@ void QHttpThreadDelegate::headerChangedSlot()
// Is using a zerocopy buffer allowed by user and possible with this reply?
if (httpReply->supportsUserProvidedDownloadBuffer()
&& (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
- QT_TRY {
- char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
- if (buf) {
- downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
- httpReply->setUserProvidedDownloadBuffer(buf);
- }
- } QT_CATCH(const std::bad_alloc &) {
- // in out of memory situations, don't use downloadbuffer.
+ char *buf = new (std::nothrow) char[httpReply->contentLength()];
+ // in out of memory situations, don't use downloadBuffer.
+ if (buf) {
+ downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
+ httpReply->setUserProvidedDownloadBuffer(buf);
}
}
@@ -652,7 +621,8 @@ void QHttpThreadDelegate::headerChangedSlot()
isPipeliningUsed = httpReply->isPipeliningUsed();
incomingContentLength = httpReply->contentLength();
removedContentLength = httpReply->removedContentLength();
- isSpdyUsed = httpReply->isSpdyUsed();
+ isHttp2Used = httpReply->isHttp2Used();
+ isCompressed = httpReply->isCompressed();
emit downloadMetaData(incomingHeaders,
incomingStatusCode,
@@ -661,7 +631,8 @@ void QHttpThreadDelegate::headerChangedSlot()
downloadBuffer,
incomingContentLength,
removedContentLength,
- isSpdyUsed);
+ isHttp2Used,
+ isCompressed);
}
void QHttpThreadDelegate::synchronousHeaderChangedSlot()
@@ -677,7 +648,7 @@ void QHttpThreadDelegate::synchronousHeaderChangedSlot()
incomingStatusCode = httpReply->statusCode();
incomingReasonPhrase = httpReply->reasonPhrase();
isPipeliningUsed = httpReply->isPipeliningUsed();
- isSpdyUsed = httpReply->isSpdyUsed();
+ isHttp2Used = httpReply->isHttp2Used();
incomingContentLength = httpReply->contentLength();
}
@@ -782,3 +753,5 @@ void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNet
#endif
QT_END_NAMESPACE
+
+#include "moc_qhttpthreaddelegate_p.cpp"
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 355d1afc30..38e9fb4d78 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPTHREADDELEGATE_H
#define QHTTPTHREADDELEGATE_H
@@ -62,12 +26,14 @@
#include <QNetworkReply>
#include "qhttpnetworkrequest_p.h"
#include "qhttpnetworkconnection_p.h"
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include <QSharedPointer>
#include <QScopedPointer>
#include "private/qnoncontiguousbytedevice_p.h"
#include "qnetworkaccessauthenticationmanager_p.h"
#include <QtNetwork/private/http2protocol_p.h>
+#include <QtNetwork/qhttpheaders.h>
QT_REQUIRE_CONFIG(http);
@@ -97,30 +63,31 @@ public:
qint64 readBufferMaxSize;
qint64 bytesEmitted;
// From backend, modified by us for signal compression
- QSharedPointer<QAtomicInt> pendingDownloadData;
- QSharedPointer<QAtomicInt> pendingDownloadProgress;
+ std::shared_ptr<QAtomicInt> pendingDownloadData;
+ std::shared_ptr<QAtomicInt> pendingDownloadProgress;
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy cacheProxy;
QNetworkProxy transparentProxy;
#endif
- QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+ std::shared_ptr<QNetworkAccessAuthenticationManager> authenticationManager;
bool synchronous;
+ qint64 connectionCacheExpiryTimeoutSeconds;
// outgoing, Retrieved in the synchronous HTTP case
QByteArray synchronousDownloadData;
- QList<QPair<QByteArray,QByteArray> > incomingHeaders;
+ QHttpHeaders incomingHeaders;
int incomingStatusCode;
QString incomingReasonPhrase;
bool isPipeliningUsed;
- bool isSpdyUsed;
+ bool isHttp2Used;
qint64 incomingContentLength;
qint64 removedContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
+ QHttp1Configuration http1Parameters;
QHttp2Configuration http2Parameters;
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession;
-#endif
+
+ bool isCompressed;
protected:
// The zerocopy download buffer, if used:
@@ -144,8 +111,10 @@ signals:
void sslConfigurationChanged(const QSslConfiguration &);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
#endif
- void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
- QSharedPointer<char>, qint64, qint64, bool);
+ void socketStartedConnecting();
+ void requestSent();
+ void downloadMetaData(const QHttpHeaders &, int, const QString &, bool,
+ QSharedPointer<char>, qint64, qint64, bool, bool);
void downloadProgress(qint64, qint64);
void downloadData(const QByteArray &);
void error(QNetworkReply::NetworkError, const QString &);
@@ -196,22 +165,18 @@ class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevic
{
Q_OBJECT
protected:
- bool wantDataPending;
- qint64 m_amount;
- char *m_data;
+ bool wantDataPending = false;
+ qint64 m_amount = 0;
+ char *m_data = nullptr;
QByteArray m_dataArray;
- bool m_atEnd;
- qint64 m_size;
- qint64 m_pos; // to match calls of haveDataSlot with the expected position
+ bool m_atEnd = false;
+ qint64 m_size = 0;
+ qint64 m_pos = 0; // to match calls of haveDataSlot with the expected position
public:
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
: QNonContiguousByteDevice(),
- wantDataPending(false),
- m_amount(0),
- m_data(nullptr),
m_atEnd(aE),
- m_size(s),
- m_pos(0)
+ m_size(s)
{
}
@@ -284,6 +249,7 @@ public:
if (b) {
// the reset succeeded, we're at pos 0 again
m_pos = 0;
+ m_atEnd = false;
// the HTTP code will anyway abort the request if !b.
}
return b;
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
index 0df11684b1..ab7c27b885 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager.cpp
+++ b/src/network/access/qnetworkaccessauthenticationmanager.cpp
@@ -1,49 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessauthenticationmanager_p.h"
#include "qnetworkaccessmanager.h"
#include "qnetworkaccessmanager_p.h"
#include "QtCore/qbuffer.h"
+#include "QtCore/qlist.h"
#include "QtCore/qurl.h"
-#include "QtCore/qvector.h"
#include "QtCore/QMutexLocker"
#include "QtNetwork/qauthenticator.h"
@@ -51,11 +15,10 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
-
-
-class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
- public QNetworkAccessCache::CacheableObject
+class QNetworkAuthenticationCache : private QList<QNetworkAuthenticationCredential>,
+ public QNetworkAccessCache::CacheableObject
{
public:
QNetworkAuthenticationCache()
@@ -65,20 +28,23 @@ public:
reserve(1);
}
- QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
+ using QList<QNetworkAuthenticationCredential>::begin;
+ using QList<QNetworkAuthenticationCredential>::end;
+
+ iterator findClosestMatch(const QString &domain)
{
iterator it = std::lower_bound(begin(), end(), domain);
if (it == end() && !isEmpty())
--it;
if (it == end() || !domain.startsWith(it->domain))
- return nullptr;
- return &*it;
+ return end();
+ return it;
}
void insert(const QString &domain, const QString &user, const QString &password)
{
- QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
- if (closestMatch && closestMatch->domain == domain) {
+ iterator closestMatch = findClosestMatch(domain);
+ if (closestMatch != end() && closestMatch->domain == domain) {
// we're overriding the current credentials
closestMatch->user = user;
closestMatch->password = password;
@@ -88,10 +54,10 @@ public:
newCredential.user = user;
newCredential.password = password;
- if (closestMatch)
- QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
+ if (closestMatch != end())
+ QList<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
else
- QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
+ QList<QNetworkAuthenticationCredential>::insert(end(), newCredential);
}
}
@@ -105,16 +71,16 @@ static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QStri
switch (proxy.type()) {
case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
+ key.setScheme("proxy-socks5"_L1);
break;
case QNetworkProxy::HttpProxy:
case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
+ key.setScheme("proxy-http"_L1);
break;
case QNetworkProxy::FtpCachingProxy:
- key.setScheme(QLatin1String("proxy-ftp"));
+ key.setScheme("proxy-ftp"_L1);
break;
case QNetworkProxy::DefaultProxy:
@@ -290,9 +256,9 @@ QNetworkAccessAuthenticationManager::fetchCachedCredentials(const QUrl &url,
QNetworkAuthenticationCache *auth =
static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
- QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
+ auto cred = auth->findClosestMatch(url.path());
QNetworkAuthenticationCredential ret;
- if (cred)
+ if (cred != auth->end())
ret = *cred;
authenticationCache.releaseEntry(cacheKey);
return ret;
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index 31111ca2a5..f88360f1c1 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
#define QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
@@ -54,7 +18,6 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessmanager.h"
#include "qnetworkaccesscache_p.h"
-#include "qnetworkaccessbackend_p.h"
#include "QtNetwork/qnetworkproxy.h"
#include "QtCore/QMutex"
@@ -75,7 +38,7 @@ public:
return domain.isNull() && user.isNull() && password.isNull();
}
};
-Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_RELOCATABLE_TYPE);
inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
{ return t1.domain < t2; }
inline bool operator<(const QString &t1, const QNetworkAuthenticationCredential &t2)
@@ -86,7 +49,7 @@ inline bool operator<(const QNetworkAuthenticationCredential &t1, const QNetwork
class QNetworkAccessAuthenticationManager
{
public:
- QNetworkAccessAuthenticationManager() { };
+ QNetworkAccessAuthenticationManager() {}
void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url,
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 8f42f3690b..3c7fee567d 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -1,51 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessbackend_p.h"
+#include "qnetworkreplyimpl_p.h"
#include "qnetworkaccessmanager_p.h"
-#include "qnetworkconfigmanager.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "QtCore/qmutex.h"
#include "QtCore/qstringlist.h"
-#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkaccesscachebackend_p.h"
#include "qabstractnetworkcache.h"
@@ -73,24 +36,26 @@ public:
static QBasicAtomicInt valid;
};
Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
-QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
-QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
-{
- QMutexLocker locker(&factoryData()->mutex);
- factoryData()->append(this);
-}
-
-QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
+class QNetworkAccessBackendPrivate : public QObjectPrivate
{
- if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
- QMutexLocker locker(&factoryData()->mutex);
- factoryData()->removeAll(this);
- }
-}
+public:
+ QNetworkAccessBackend::TargetTypes m_targetTypes;
+ QNetworkAccessBackend::SecurityFeatures m_securityFeatures;
+ QNetworkAccessBackend::IOFeatures m_ioFeatures;
+ std::shared_ptr<QNonContiguousByteDevice> uploadByteDevice;
+ QIODevice *wrappedUploadByteDevice;
+ QNetworkReplyImplPrivate *m_reply = nullptr;
+ QNetworkAccessManagerPrivate *m_manager = nullptr;
+
+ bool m_canCache = false;
+ bool m_isSynchronous = false;
+};
-QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
- const QNetworkRequest &request)
+QNetworkAccessBackend *
+QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request)
{
if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
QMutexLocker locker(&factoryData()->mutex);
@@ -99,7 +64,7 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
while (it != end) {
QNetworkAccessBackend *backend = (*it)->create(op, request);
if (backend) {
- backend->manager = this;
+ backend->setManagerPrivate(this);
return backend; // found a factory that handled our request
}
++it;
@@ -124,284 +89,773 @@ QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const
return QStringList();
}
-QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
+/*!
+ \class QNetworkAccessBackendFactory
+ \brief QNetworkAccessBackendFactory is the base class to inherit
+ from for Qt to instantiate and query your QNetworkAccessBackend
+ plugin.
+ \since 6.0
+ \internal
+
+//! [semi-private-notice]
+ The class is considered semi-private and as such requires linking
+ to "NetworkPrivate" to access the header. Furthermore it means
+ the class is not under the same binary compatibility restrictions
+ as the rest of Qt. While we still try to avoid breakage it may
+ still occur. The class is primarily meant to be used by plugins
+ which would be recompiled every time Qt is updated.
+//! [semi-private-notice]
+
+ This class acts as the primary interface to the plugin and must
+ be derived from. It deals with both querying supported schemes
+ and the creation of QNetworkAccessBackend
+
+ Since they are both abstract function you are required to
+ implement supportedSchemes() and create().
+*/
+
+/*!
+ \fn QStringList QNetworkAccessBackendFactory::supportedSchemes() const
+
+ Override this method in your own derived class to let Qt know
+ what schemes your class can handle.
+*/
+
+/*!
+ \fn QNetworkAccessBackendFactory::create(QNetworkAccessManager::Operation op, const QNetworkRequest &request) const
+
+ Override this method in your own class and return a
+ heap-allocated instance of your class derived from
+ QNetworkAccessBackend.
+
+ If \a op or a property of \a request is not supported (for
+ example the URL's scheme) then you must return \nullptr.
+
+ \sa QNetworkRequest::attribute(), QNetworkRequest::url(), QUrl::scheme()
+*/
+
+/*!
+ \class QNetworkAccessBackend
+ \brief QNetworkAccessBackend is the base class for implementing
+ support for schemes used by QNetworkAccessManager.
+ \since 6.0
+ \internal
+
+ \include access/qnetworkaccessbackend.cpp semi-private-notice
+
+ This class can be derived from to add support for further schemes
+ in QNetworkAccessManager.
+
+ The design of QNetworkAccessBackend makes it possible to specialize
+ behavior as needed for certain backends.
+ This was done using the (currently) 3 enums TargetType,
+ SecurityFeatures and IOFeatures. For example while only open()
+ and close() are abstract functions you are also required to
+ implement either read() or readPointer() and advanceReadPointer()
+ depending on whether you enable IOFeature::ZeroCopy or not.
+ Read more about it in the documentation for each of the
+ enumerators.
+
+ \sa TargetType, SecurityFeatures, IOFeatures
+*/
+
+/*!
+ \enum QNetworkAccessBackend::TargetType
+
+ Use the values in this enum to specify what type of target
+ the plugin supports. Setting the right type can be important,
+ for example: proxyList() is only available for a Networked
+ plugin.
+
+ \value Networked
+ The plugin supports and expect to connect to networked
+ resources. E.g. over TCP, UDP or similar.
+ \value Local
+ The plugin supports and expects to access local files,
+ generate data and/or locally connected devices.
+*/
+
+/*!
+ \enum QNetworkAccessBackend::SecurityFeature
+
+ Use the values in this enum to specify what type of security
+ features the plugin may utilize. Setting the right type(s)
+ can be important, for example: setSslConfiguration() may not
+ be called for any plugin that do not claim to support TLS.
+
+ \value None
+ No specific features are claimed to be supported.
+ \value TLS
+ The plugin supports and expects to use TLS.
+*/
+
+/*!
+ \enum QNetworkAccessBackend::IOFeature
+
+ Use the values in this enum to specify what type of IO
+ features the plugin may utilize.
+
+ \value None
+ No specific features are claimed to be supported.
+ \value ZeroCopy
+ The plugin will have raw data available in contiguous
+ segments and can return a pointer to the data at request.
+ Claiming to support this requires implementing readPointer()
+ and advanceReadPointer().
+ \value NeedResetableUpload
+ The plugin may encounter scenarios where data to upload that
+ has already been consumed needs to be restored and re-sent.
+ E.g. some data was consumed and sent before a redirect
+ response was received, and after the redirect the
+ previously-consumed data needs to be re-sent.
+ \omitvalue SupportsSynchronousMode
+*/
+
+/*!
+ Constructs the QNetworkAccessBackend.
+ You can opt in to specific backend behaviors with \a targetTypes,
+ \a securityFeatures and \a ioFeatures.
+ See their respective enums and values for more information.
+
+ \sa TargetType, SecurityFeature, IOFeature
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
+ SecurityFeatures securityFeatures,
+ IOFeatures ioFeatures)
+ : QObject(*(new QNetworkAccessBackendPrivate), nullptr)
{
- if (reply->outgoingDataBuffer)
- uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingDataBuffer);
- else if (reply->outgoingData) {
- uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingData);
- } else {
- return nullptr;
- }
+ Q_D(QNetworkAccessBackend);
+ d->m_targetTypes = targetTypes;
+ d->m_securityFeatures = securityFeatures;
+ d->m_ioFeatures = ioFeatures;
+}
- // We want signal emissions only for normal asynchronous uploads
- if (!isSynchronous())
- connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes)
+ : QNetworkAccessBackend(targetTypes, SecurityFeature::None, IOFeature::None)
+{
+}
- return uploadByteDevice.data();
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
+ SecurityFeatures securityFeatures)
+ : QNetworkAccessBackend(targetTypes, securityFeatures, IOFeature::None)
+{
}
-// need to have this function since the reply is a private member variable
-// and the special backends need to access this.
-void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes, IOFeatures ioFeatures)
+ : QNetworkAccessBackend(targetTypes, SecurityFeature::None, ioFeatures)
{
- if (reply->isFinished)
- return;
- reply->emitUploadProgress(bytesSent, bytesTotal);
}
-QNetworkAccessBackend::QNetworkAccessBackend()
- : manager(nullptr)
- , reply(nullptr)
- , synchronous(false)
+/*!
+ Destructs the QNetworkAccessBackend base class.
+*/
+QNetworkAccessBackend::~QNetworkAccessBackend() { }
+
+/*!
+ Returns the security related features that the backend claims to
+ support.
+
+ \sa SecurityFeature
+*/
+QNetworkAccessBackend::SecurityFeatures QNetworkAccessBackend::securityFeatures() const noexcept
{
+ return d_func()->m_securityFeatures;
}
-QNetworkAccessBackend::~QNetworkAccessBackend()
+/*!
+ Returns the TargetTypes that the backend claims to target.
+
+ \sa TargetType
+*/
+QNetworkAccessBackend::TargetTypes QNetworkAccessBackend::targetTypes() const noexcept
{
+ return d_func()->m_targetTypes;
}
-void QNetworkAccessBackend::downstreamReadyWrite()
+/*!
+ Returns the I/O features that the backend claims to support.
+
+ \sa IOFeature
+*/
+QNetworkAccessBackend::IOFeatures QNetworkAccessBackend::ioFeatures() const noexcept
+{
+ return d_func()->m_ioFeatures;
+}
+
+/*!
+ Prepares the backend and calls open().
+ E.g. for TargetType::Networked it will prepare proxyList().
+
+ \sa TargetType, targetTypes
+*/
+bool QNetworkAccessBackend::start()
{
- // do nothing
+ Q_D(QNetworkAccessBackend);
+#ifndef QT_NO_NETWORKPROXY
+ if (targetTypes() & QNetworkAccessBackend::TargetType::Networked)
+ d->m_reply->proxyList = d->m_manager->queryProxy(QNetworkProxyQuery(url()));
+#endif
+
+ // now start the request
+ open();
+ return true;
}
-void QNetworkAccessBackend::setDownstreamLimited(bool b)
+/*!
+ \fn void QNetworkAccessBackend::open() = 0
+
+ You must implement this in your derived class.
+ During this call you must open the connection and begin the request
+ (see: request()).
+
+ As the connection progresses you must call the various public and
+ protected slots on QNetworkAccessBackend. As an example, when you have
+ received some data you must call readyRead(). And when all the data has been
+ received you must call finished(). This could, for example, be done by
+ binding signals inside your own implementation to the slots, or by calling
+ them directly.
+
+ \sa close()
+*/
+
+/*!
+ \fn void QNetworkAccessBackend::close() = 0
+
+ You must implement this function in your derived class.
+ This function gets called when the QNetworkReply is closed or aborted.
+
+ You should not emit an error or call finished() during this call since
+ QtNetwork will set and emit the \c{QNetworkReply::OperationCanceledError}
+ error by itself after control flow returns from this function.
+*/
+
+/*!
+ \fn qint64 QNetworkAccessBackend::bytesAvailable() const = 0
+
+ You must implement this function in your derived class.
+ This function is called at various times. It may be called because the user
+ called QNetworkReply::bytesAvailable(), and it may be called before an
+ attempt to read is made.
+
+ While this function doesn't technically need to return an accurate number,
+ it may result in reduced performance if it does not. This function must
+ return zero if there are no bytes available.
+*/
+
+#if QT_CONFIG(ssl)
+/*!
+ Passes a \a configuration with the user's desired TLS
+ configuration. If you don't have the TLS security feature this
+ may not be called.
+
+ \sa SecurityFeature, securityFeatures
+*/
+void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &configuration)
{
- Q_UNUSED(b);
- // do nothing
+ Q_UNUSED(configuration);
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden setSslConfiguration.",
+ metaObject()->className());
+ }
}
-void QNetworkAccessBackend::copyFinished(QIODevice *)
+/*!
+ Override this and return the QSslConfiguration used if you
+ have the TLS security feature
+
+ \sa SecurityFeature, securityFeatures
+*/
+QSslConfiguration QNetworkAccessBackend::sslConfiguration() const
{
- // do nothing
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden sslConfiguration.",
+ metaObject()->className());
+ }
+ return {};
}
+#endif
+
+/*!
+ This function will be called when the user wants to ignore
+ all TLS handshake errors. Derive this function if TLS is
+ supported.
+ \sa SecurityFeature, securityFeatures
+*/
void QNetworkAccessBackend::ignoreSslErrors()
{
- // do nothing
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
+ metaObject()->className());
+ }
}
+/*!
+ This function will be called when the user wants to ignore
+ specific \a errors. Derive this function if TLS is supported.
+
+ \sa SecurityFeature, securityFeatures
+*/
void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
{
Q_UNUSED(errors);
- // do nothing
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
+ metaObject()->className());
+ }
}
-void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
-{
- // do nothing
-}
+/*!
+ The data which the returned value views must stay valid until
+ at least the next call to a non-const function. advanceReadPointer
+ will be called if any of the data was used.
-void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
+ Note: This will only be called if IOFeature::ZeroCopy was
+ specified in the call to the constructor.
+
+ \sa advanceReadPointer, read
+*/
+QByteArrayView QNetworkAccessBackend::readPointer()
{
- // do nothing
+ if (ioFeatures() & IOFeature::ZeroCopy) {
+ qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden readPointer.",
+ metaObject()->className());
+ }
+ return {};
}
-QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
+/*!
+ This function is to notify your class that \a distance
+ bytes have been read using readPointer and next time
+ readPointer() is called those bytes should not be included.
+
+ Note: This will only be called if IOFeature::ZeroCopy was
+ specified in the call to the constructor.
+
+ \sa readPointer
+*/
+void QNetworkAccessBackend::advanceReadPointer(qint64 distance)
{
- return QNetworkCacheMetaData();
+ Q_UNUSED(distance);
+ if (ioFeatures() & IOFeature::ZeroCopy) {
+ qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden advanceReadPointer.",
+ metaObject()->className());
+ }
}
-QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
+/*!
+ Implement this function to support reading from the resource
+ made available by your plugin.
+ Store data in \a data, up to a maximum of \a maxlen bytes.
+ Then return the total amount of bytes that was copied.
+
+ \sa readPointer, wantToRead
+*/
+qint64 QNetworkAccessBackend::read(char *data, qint64 maxlen)
{
- return reply->operation;
+ Q_UNUSED(data);
+ Q_UNUSED(maxlen);
+ if ((ioFeatures() & IOFeature::ZeroCopy) == 0) {
+ qWarning("Backend (%s) is not ZeroCopy and has not implemented read(...)!",
+ metaObject()->className());
+ }
+ return 0;
}
-QNetworkRequest QNetworkAccessBackend::request() const
+/*!
+ This is called before we read if there are no bytes available
+ and we are ready to read more. Return \c true if new data was
+ made available.
+
+ \sa read, readPointer
+*/
+bool QNetworkAccessBackend::wantToRead()
{
- return reply->request;
+ // Base implementation does nothing
+ return false;
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
+/*!
+ Returns a list of proxies configured for the URL returned by
+ url().
+
+ It is only valid to call this function if TargetType::Networked
+ was specified in the call to the constructor.
+*/
QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
{
- return reply->proxyList;
+ Q_ASSERT(targetTypes() & TargetType::Networked);
+ return d_func()->m_reply->proxyList;
}
#endif
-QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
+/*!
+ Returns the current URL of the reply
+*/
+QUrl QNetworkAccessBackend::url() const
{
- if (!manager)
- return nullptr;
- return manager->networkCache;
+ return d_func()->m_reply->url;
}
-void QNetworkAccessBackend::setCachingEnabled(bool enable)
+/*!
+ Sets the URL of the reply. This could e.g. be needed if a
+ redirect or similar was performed.
+*/
+void QNetworkAccessBackend::setUrl(const QUrl &url)
{
- reply->setCachingEnabled(enable);
+ d_func()->m_reply->url = url;
}
-bool QNetworkAccessBackend::isCachingEnabled() const
+/*!
+ Returns the value of the \a header.
+ If no such header was known it returns a default-constructed
+ QVariant.
+
+ \sa setHeader, rawHeader, setRawHeader
+*/
+QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
{
- return reply->isCachingEnabled();
+ return d_func()->m_reply->cookedHeaders.value(header);
}
-qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
+/*!
+ Sets the value of the \a header to \a value.
+ This can be queried on the QNetworkReply instance which was
+ returned when calling one of the appropriate functions on
+ QNetworkAccessManager.
+
+ \sa header, rawHeader, setRawHeader
+*/
+void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
{
- return reply->nextDownstreamBlockSize();
+ d_func()->m_reply->setCookedHeader(header, value);
}
-void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
+/*!
+ Returns the value of the \a header.
+ If no such header was known it returns a default-constructed
+ QVariant.
+
+ \sa setHeader, rawHeader, setRawHeader
+*/
+QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &header) const
{
- reply->appendDownstreamData(list);
+ return d_func()->m_reply->q_func()->rawHeader(header);
}
-void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
+/*!
+ Sets the value of the \a header to \a value.
+
+ This value is accessible on the QNetworkReply instance which was
+ returned when calling one of the appropriate functions on
+ QNetworkAccessManager.
+
+ \sa header, rawHeader, setRawHeader
+*/
+void QNetworkAccessBackend::setRawHeader(const QByteArray &header, const QByteArray &value)
{
- reply->appendDownstreamData(data);
+ d_func()->m_reply->setRawHeader(header, value);
}
-// not actually appending data, it was already written to the user buffer
-void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+/*!
+ \since 6.8
+
+ Returns headers that are set in this QNetworkAccessBackend instance.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkAccessBackend::headers() const
{
- reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
+ return d_func()->m_reply->headers();
}
-char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers, overriding any previously set headers.
+
+ These headers are accessible on the QNetworkReply instance which was
+ returned when calling one of the appropriate functions on
+ QNetworkAccessManager.
+
+ \sa headers()
+*/
+void QNetworkAccessBackend::setHeaders(QHttpHeaders &&newHeaders)
{
- return reply->getDownloadBuffer(size);
+ d_func()->m_reply->setHeaders(std::move(newHeaders));
}
-QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkAccessBackend::setHeaders(const QHttpHeaders &newHeaders)
{
- return reply->q_func()->header(header);
+ d_func()->m_reply->setHeaders(newHeaders);
}
-void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+/*!
+ Returns the operation which was requested when calling
+ QNetworkAccessManager.
+*/
+QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
{
- reply->setCookedHeader(header, value);
+ return d_func()->m_reply->operation;
}
-bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
+/*!
+ Returns \c true if setCachingEnabled was previously called with \c true.
+ Returns \c false otherwise, which is the default value.
+
+ \sa setCachingEnabled
+*/
+bool QNetworkAccessBackend::isCachingEnabled() const
{
- return reply->q_func()->hasRawHeader(headerName);
+ return d_func()->m_canCache;
}
-QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
+/*!
+ If \a canCache is \c true then this hints to us that we can cache
+ the reply that is created.
+
+ \sa isCachingEnabled
+*/
+void QNetworkAccessBackend::setCachingEnabled(bool canCache)
{
- return reply->q_func()->rawHeader(headerName);
+ d_func()->m_canCache = canCache;
}
-QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
+/*!
+ Set \a attribute to \a value. If \c{value.isValid()} returns
+ \c false then the attribute is unset.
+
+ This value is accessible on the QNetworkReply instance which was
+ returned when calling one of the appropriate functions on
+ QNetworkAccessManager.
+*/
+void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute attribute,
+ const QVariant &value)
{
- return reply->q_func()->rawHeaderList();
+ Q_D(QNetworkAccessBackend);
+ if (value.isValid())
+ d->m_reply->attributes.insert(attribute, value);
+ else
+ d->m_reply->attributes.remove(attribute);
}
-void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+/*!
+ Creates a QIODevice for the data provided to upload, if any.
+
+ Emission of upload progress is handled internally as the device
+ gets read from.
+
+ Returns a pointer to a device with data or nullptr if there was
+ no data to upload.
+*/
+QIODevice *QNetworkAccessBackend::createUploadByteDevice()
{
- reply->setRawHeader(headerName, headerValue);
+ Q_D(QNetworkAccessBackend);
+
+ if (d->m_reply->outgoingDataBuffer)
+ d->uploadByteDevice =
+ QNonContiguousByteDeviceFactory::createShared(d->m_reply->outgoingDataBuffer);
+ else if (d->m_reply->outgoingData) {
+ d->uploadByteDevice =
+ QNonContiguousByteDeviceFactory::createShared(d->m_reply->outgoingData);
+ } else {
+ return nullptr;
+ }
+
+ // We want signal emissions only for normal asynchronous uploads
+ if (!isSynchronous()) {
+ connect(d->uploadByteDevice.get(), &QNonContiguousByteDevice::readProgress, this,
+ [this](qint64 a, qint64 b) {
+ Q_D(QNetworkAccessBackend);
+ if (!d->m_reply->isFinished)
+ d->m_reply->emitUploadProgress(a, b);
+ });
+ }
+
+ d->wrappedUploadByteDevice = QNonContiguousByteDeviceFactory::wrap(d->uploadByteDevice.get());
+ return d->wrappedUploadByteDevice;
}
-QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
+/*!
+ Returns the upload byte device associated with the current
+ request. This does not create the request but simply returns
+ the pointer stored in this base class so it doesn't need to be
+ stored in the subclass too.
+*/
+QIODevice *QNetworkAccessBackend::uploadByteDevice()
{
- return reply->q_func()->attribute(code);
+ return d_func()->wrappedUploadByteDevice;
}
-void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
+/*!
+ \internal
+ Returns \c true if synchronous mode is enabled.
+ If it is disabled or not supported it will return \c {false}.
+*/
+bool QNetworkAccessBackend::isSynchronous() const
{
- if (value.isValid())
- reply->attributes.insert(code, value);
- else
- reply->attributes.remove(code);
+ return d_func()->m_isSynchronous;
}
-QUrl QNetworkAccessBackend::url() const
+
+/*!
+ \internal
+ Enables or disables synchronous mode depending on \a synchronous
+ if the backend supports it. Otherwise it will always be disabled.
+*/
+void QNetworkAccessBackend::setSynchronous(bool synchronous)
{
- return reply->url;
+ if ((ioFeatures() & IOFeature::SupportsSynchronousMode) == 0)
+ return;
+ d_func()->m_isSynchronous = synchronous;
}
-void QNetworkAccessBackend::setUrl(const QUrl &url)
+/*!
+ Call this slot when you have more data available to notify
+ the backend that we can attempt to read again.
+*/
+void QNetworkAccessBackend::readyRead()
{
- reply->url = url;
+ d_func()->m_reply->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
}
+/*!
+ Call this slot when there will be no more data available,
+ regardless of whether the transfer was successful or unsuccessful.
+ For unsuccessful transfers make sure to call error() first!
+*/
void QNetworkAccessBackend::finished()
{
- reply->finished();
+ d_func()->m_reply->finished();
}
+/*!
+ Call this slot if an error occurs. An error would be something
+ you cannot recover from (e.g. the file requested is missing).
+ The \a code and \a errorString is transferred to and stored in
+ the QNetworkReply and the \a code is emitted through the
+ QNetworkReply::errorOccurred() signal.
+*/
void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
{
- reply->error(code, errorString);
+ Q_ASSERT(!d_func()->m_reply->isFinished);
+ d_func()->m_reply->error(code, errorString);
}
#ifndef QT_NO_NETWORKPROXY
+/*!
+ Call this slot if, when connecting through a proxy, it requests
+ authentication. This may cause the
+ QNetworkAccessManager::proxyAuthenticationRequired() signal to be
+ emitted if the credentials are not already stored in an internal
+ cache.
+ To be able to make the lookup in the cache and potentially the
+ subsequent request the \a proxy needs to be known. The credentials
+ will be stored in \a authenticator. While \a authenticator is a
+ pointer, passing \c nullptr is invalid.
+*/
void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
QAuthenticator *authenticator)
{
- manager->proxyAuthenticationRequired(QUrl(), proxy, synchronous, authenticator, &reply->lastProxyAuthentication);
+ Q_D(QNetworkAccessBackend);
+ Q_ASSERT(authenticator);
+ d->m_manager->proxyAuthenticationRequired(QUrl(), proxy, isSynchronous(), authenticator,
+ &d->m_reply->lastProxyAuthentication);
}
#endif
+/*!
+ Call this slot if the remote resource requests authentication.
+ This may cause the
+ QNetworkAccessManager::authenticationRequired() signal to be
+ emitted if the credentials are not already stored in an internal
+ cache.
+ The credentials will be stored in \a authenticator. While
+ \a authenticator is a pointer, passing \c nullptr is invalid.
+*/
void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
{
- manager->authenticationRequired(authenticator, reply->q_func(), synchronous, reply->url, &reply->urlForLastAuthentication);
+ Q_D(QNetworkAccessBackend);
+ Q_ASSERT(authenticator);
+ d->m_manager->authenticationRequired(authenticator, d->m_reply->q_func(), isSynchronous(),
+ d->m_reply->url, &d->m_reply->urlForLastAuthentication);
}
+/*!
+ Call this slot, if appropriate, after having processed and
+ updated metadata (e.g. headers).
+*/
void QNetworkAccessBackend::metaDataChanged()
{
- reply->metaDataChanged();
+ d_func()->m_reply->metaDataChanged();
}
-void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
+/*!
+ Call this slot if, when connecting to the resource, a redirect
+ to \a destination was requested.
+*/
+void QNetworkAccessBackend::redirectionRequested(const QUrl &destination)
{
- reply->redirectionRequested(target);
+ d_func()->m_reply->redirectionRequested(destination);
}
-void QNetworkAccessBackend::encrypted()
+/*!
+ \internal
+*/
+void QNetworkAccessBackend::setReplyPrivate(QNetworkReplyImplPrivate *reply)
{
-#ifndef QT_NO_SSL
- reply->encrypted();
-#endif
+ d_func()->m_reply = reply;
}
-void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
+/*!
+ \internal
+*/
+void QNetworkAccessBackend::setManagerPrivate(QNetworkAccessManagerPrivate *manager)
{
-#ifndef QT_NO_SSL
- reply->sslErrors(errors);
-#else
- Q_UNUSED(errors);
-#endif
+ d_func()->m_manager = manager;
}
/*!
- Starts the backend. Returns \c true if the backend is started. Returns \c false if the backend
- could not be started due to an unopened or roaming session. The caller should recall this
- function once the session has been opened or the roaming process has finished.
+ Returns the network cache object that was available when the
+ request was started. Returns \c nullptr if none was available.
*/
-bool QNetworkAccessBackend::start()
+QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
{
-#ifndef QT_NO_BEARERMANAGEMENT
- // For bearer, check if session start is required
- QSharedPointer<QNetworkSession> networkSession(manager->getNetworkSession());
- if (networkSession) {
- // session required
- if (networkSession->isOpen() &&
- networkSession->state() == QNetworkSession::Connected) {
- // Session is already open and ready to use.
- // copy network session down to the backend
- setProperty("_q_networksession", QVariant::fromValue(networkSession));
- } else {
- // Session not ready, but can skip for loopback connections
-
- // This is not ideal.
- // Don't need an open session for localhost access.
- if (!reply->url.isLocalFile()) {
- const QString host = reply->url.host();
- if (host != QLatin1String("localhost") && !QHostAddress(host).isLoopback())
- return false; // need to wait for session to be opened
- }
- }
- }
-#endif
-
-#ifndef QT_NO_NETWORKPROXY
- reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
-#endif
+ return d_func()->m_manager->networkCache;
+}
- // now start the request
- open();
- return true;
+// -- QNetworkAccessBackendFactory
+/*!
+ Constructs QNetworkAccessBackendFactory
+*/
+QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
+{
+ if (factoryData())
+ factoryData->append(this);
}
+/*!
+ Destructs QNetworkAccessBackendFactory
+*/
+QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
+{
+ if (factoryData.exists())
+ factoryData->removeAll(this);
+};
+
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessbackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 4b5422ce29..4f8d7e4372 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSBACKEND_P_H
#define QNETWORKACCESSBACKEND_P_H
@@ -44,148 +8,119 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the Network Access API. This header file may change from
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworkreplyimpl_p.h"
-#include "QtCore/qobject.h"
+#include <QtNetwork/qtnetworkglobal.h>
-QT_BEGIN_NAMESPACE
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkreply.h>
-class QAuthenticator;
-class QNetworkProxy;
-class QNetworkProxyQuery;
-class QNetworkRequest;
-class QStringList;
-class QUrl;
-class QSslConfiguration;
+#include <QtCore/qobject.h>
+#include <QtCore/qflags.h>
+#include <QtCore/qbytearrayview.h>
+#include <QtCore/private/qglobal_p.h>
-class QNetworkAccessManagerPrivate;
-class QNetworkReplyImplPrivate;
-class QAbstractNetworkCache;
-class QNetworkCacheMetaData;
-class QNonContiguousByteDevice;
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
-// Should support direct file upload from disk or download to disk.
-//
-// - The HTTP handler will use two QIODevices for communication (pull mechanism)
-// - KIO uses a pull mechanism too (data/dataReq signals)
-class QNetworkAccessBackend : public QObject
+QT_BEGIN_NAMESPACE
+
+class QNetworkReplyImplPrivate;
+class QNetworkAccessManagerPrivate;
+class QNetworkAccessBackendPrivate;
+class Q_NETWORK_EXPORT QNetworkAccessBackend : public QObject
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QNetworkAccessBackend);
+
public:
- QNetworkAccessBackend();
+ enum class TargetType {
+ Networked = 0x1, // We need to query for proxy in case it is needed
+ Local = 0x2, // Local file, generated data or local device
+ };
+ Q_ENUM(TargetType)
+ Q_DECLARE_FLAGS(TargetTypes, TargetType)
+
+ enum class SecurityFeature {
+ None = 0x0,
+ TLS = 0x1, // We need to set QSslConfiguration
+ };
+ Q_ENUM(SecurityFeature)
+ Q_DECLARE_FLAGS(SecurityFeatures, SecurityFeature)
+
+ enum class IOFeature {
+ None = 0x0,
+ ZeroCopy = 0x1, // readPointer and advanceReadPointer() is available!
+ NeedResetableUpload = 0x2, // Need to buffer upload data
+ SupportsSynchronousMode = 0x4, // Used for XMLHttpRequest
+ };
+ Q_ENUM(IOFeature)
+ Q_DECLARE_FLAGS(IOFeatures, IOFeature)
+
+ QNetworkAccessBackend(TargetTypes targetTypes, SecurityFeatures securityFeatures,
+ IOFeatures ioFeatures);
+ QNetworkAccessBackend(TargetTypes targetTypes);
+ QNetworkAccessBackend(TargetTypes targetTypes, SecurityFeatures securityFeatures);
+ QNetworkAccessBackend(TargetTypes targetTypes, IOFeatures ioFeatures);
virtual ~QNetworkAccessBackend();
- // To avoid mistaking with QIODevice names, the functions here
- // have different names. The Connection has two streams:
- //
- // - Upstream:
- // The upstream uses a QNonContiguousByteDevice provided
- // by the backend. This device emits the usual readyRead()
- // signal when the backend has data available for the connection
- // to write. The different backends can listen on this signal
- // and then pull upload data from the QNonContiguousByteDevice and
- // deal with it.
- //
- //
- // - Downstream:
- // Downstream is the data that is being read from this
- // connection and is given to the user. Downstream operates in a
- // semi-"push" mechanism: the Connection object pushes the data
- // it gets from the network, but it should avoid writing too
- // much if the data isn't being used fast enough. The value
- // returned by suggestedDownstreamBlockSize() can be used to
- // determine how much should be written at a time. The
- // downstreamBytesConsumed() function will be called when the
- // downstream buffer is consumed by the user -- the Connection
- // may choose to re-fill it with more data it has ready or get
- // more data from the network (for instance, by reading from its
- // socket).
+ SecurityFeatures securityFeatures() const noexcept;
+ TargetTypes targetTypes() const noexcept;
+ IOFeatures ioFeatures() const noexcept;
- virtual void open() = 0;
- virtual bool start();
- virtual void closeDownstreamChannel() = 0;
+ inline bool needsResetableUploadData() const noexcept
+ {
+ return ioFeatures() & IOFeature::NeedResetableUpload;
+ }
- // slot-like:
- virtual void downstreamReadyWrite();
- virtual void setDownstreamLimited(bool b);
- virtual void copyFinished(QIODevice *);
+ virtual bool start();
+ virtual void open() = 0;
+ virtual void close() = 0;
+#if QT_CONFIG(ssl)
+ virtual void setSslConfiguration(const QSslConfiguration &configuration);
+ virtual QSslConfiguration sslConfiguration() const;
+#endif
virtual void ignoreSslErrors();
virtual void ignoreSslErrors(const QList<QSslError> &errors);
+ virtual qint64 bytesAvailable() const = 0;
+ virtual QByteArrayView readPointer();
+ virtual void advanceReadPointer(qint64 distance);
+ virtual qint64 read(char *data, qint64 maxlen);
+ virtual bool wantToRead();
- virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
- virtual void setSslConfiguration(const QSslConfiguration &configuration);
-
- virtual QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
-
- // information about the request
- QNetworkAccessManager::Operation operation() const;
- QNetworkRequest request() const;
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
QList<QNetworkProxy> proxyList() const;
#endif
-
- QAbstractNetworkCache *networkCache() const;
- void setCachingEnabled(bool enable);
- bool isCachingEnabled() const;
-
- // information about the reply
QUrl url() const;
void setUrl(const QUrl &url);
-
- // "cooked" headers
QVariant header(QNetworkRequest::KnownHeaders header) const;
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ QByteArray rawHeader(const QByteArray &header) const;
+ void setRawHeader(const QByteArray &header, const QByteArray &value);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+ QNetworkAccessManager::Operation operation() const;
- // raw headers:
- bool hasRawHeader(const QByteArray &headerName) const;
- QList<QByteArray> rawHeaderList() const;
- QByteArray rawHeader(const QByteArray &headerName) const;
- void setRawHeader(const QByteArray &headerName, const QByteArray &value);
-
- // attributes:
- QVariant attribute(QNetworkRequest::Attribute code) const;
- void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
-
- bool isSynchronous() { return synchronous; }
- void setSynchronous(bool sync) { synchronous = sync; }
-
- // return true if the QNonContiguousByteDevice of the upload
- // data needs to support reset(). Currently needed for HTTP.
- // This will possibly enable buffering of the upload data.
- virtual bool needsResetableUploadData() { return false; }
-
- // Returns \c true if backend is able to resume downloads.
- virtual bool canResume() const { return false; }
- virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); }
-
- virtual bool processRequestSynchronously() { return false; }
-
-protected:
- // Create the device used for reading the upload data
- QNonContiguousByteDevice* createUploadByteDevice();
+ bool isCachingEnabled() const;
+ void setCachingEnabled(bool canCache);
- // these functions control the downstream mechanism
- // that is, data that has come via the connection and is going out the backend
- qint64 nextDownstreamBlockSize() const;
- void writeDownstreamData(QByteDataBuffer &list);
+ void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
- // not actually appending data, it was already written to the user buffer
- void writeDownstreamDataDownloadBuffer(qint64, qint64);
- char* getDownloadBuffer(qint64);
+ QIODevice *createUploadByteDevice();
+ QIODevice *uploadByteDevice();
- QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+ QAbstractNetworkCache *networkCache() const;
public slots:
- // for task 251801, needs to be a slot to be called asynchronously
- void writeDownstreamData(QIODevice *data);
-
+ void readyRead();
protected slots:
void finished();
void error(QNetworkReply::NetworkError code, const QString &errorString);
@@ -195,26 +130,21 @@ protected slots:
void authenticationRequired(QAuthenticator *auth);
void metaDataChanged();
void redirectionRequested(const QUrl &destination);
- void encrypted();
- void sslErrors(const QList<QSslError> &errors);
- void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
-
-protected:
- // FIXME In the long run we should get rid of our QNAM architecture
- // and scrap this ReplyImpl/Backend distinction.
- QNetworkAccessManagerPrivate *manager;
- QNetworkReplyImplPrivate *reply;
private:
- friend class QNetworkAccessManager;
- friend class QNetworkAccessManagerPrivate;
- friend class QNetworkReplyImplPrivate;
-
- bool synchronous;
+ void setReplyPrivate(QNetworkReplyImplPrivate *reply);
+ void setManagerPrivate(QNetworkAccessManagerPrivate *manager);
+ bool isSynchronous() const;
+ void setSynchronous(bool synchronous);
+
+ friend class QNetworkAccessManager; // for setReplyPrivate
+ friend class QNetworkAccessManagerPrivate; // for setManagerPrivate
+ friend class QNetworkReplyImplPrivate; // for {set,is}Synchronous()
};
-class QNetworkAccessBackendFactory
+class Q_NETWORK_EXPORT QNetworkAccessBackendFactory : public QObject
{
+ Q_OBJECT
public:
QNetworkAccessBackendFactory();
virtual ~QNetworkAccessBackendFactory();
@@ -223,7 +153,8 @@ public:
const QNetworkRequest &request) const = 0;
};
-QT_END_NAMESPACE
+#define QNetworkAccessBackendFactory_iid "org.qt-project.Qt.NetworkAccessBackendFactory"
+Q_DECLARE_INTERFACE(QNetworkAccessBackendFactory, QNetworkAccessBackendFactory_iid);
+QT_END_NAMESPACE
#endif
-
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
index ba092f2618..2bc0e8fb70 100644
--- a/src/network/access/qnetworkaccesscache.cpp
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -1,51 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccesscache_p.h"
#include "QtCore/qpointer.h"
-#include "QtCore/qdatetime.h"
+#include "QtCore/qdeadlinetimer.h"
#include "qnetworkaccessmanager_p.h"
#include "qnetworkreply_p.h"
#include "qnetworkrequest.h"
#include <vector>
+//#define DEBUG_ACCESSCACHE
+
QT_BEGIN_NAMESPACE
enum ExpiryTimeEnum {
@@ -63,18 +29,14 @@ namespace {
// idea copied from qcache.h
struct QNetworkAccessCache::Node
{
- QDateTime timestamp;
- std::vector<Receiver> receiverQueue;
+ QDeadlineTimer timer;
QByteArray key;
- Node *older, *newer;
- CacheableObject *object;
-
- int useCount;
+ Node *previous = nullptr; // "previous" nodes expire "previous"ly (before us)
+ Node *next = nullptr; // "next" nodes expire "next" (after us)
+ CacheableObject *object = nullptr;
- Node()
- : older(nullptr), newer(nullptr), object(nullptr), useCount(0)
- { }
+ int useCount = 0;
};
QNetworkAccessCache::CacheableObject::CacheableObject()
@@ -102,11 +64,6 @@ void QNetworkAccessCache::CacheableObject::setShareable(bool enable)
shareable = enable;
}
-QNetworkAccessCache::QNetworkAccessCache()
- : oldest(nullptr), newest(nullptr)
-{
-}
-
QNetworkAccessCache::~QNetworkAccessCache()
{
clear();
@@ -121,8 +78,9 @@ void QNetworkAccessCache::clear()
NodeHash::Iterator it = hashCopy.begin();
NodeHash::Iterator end = hashCopy.end();
for ( ; it != end; ++it) {
- it->object->key.clear();
- it->object->dispose();
+ (*it)->object->key.clear();
+ (*it)->object->dispose();
+ delete (*it);
}
// now delete:
@@ -130,7 +88,7 @@ void QNetworkAccessCache::clear()
timer.stop();
- oldest = newest = nullptr;
+ firstExpiringNode = lastExpiringNode = nullptr;
}
/*!
@@ -139,73 +97,104 @@ void QNetworkAccessCache::clear()
*/
void QNetworkAccessCache::linkEntry(const QByteArray &key)
{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end())
+ Node * const node = hash.value(key);
+ if (!node)
return;
- Node *const node = &it.value();
- Q_ASSERT(node != oldest && node != newest);
- Q_ASSERT(node->older == nullptr && node->newer == nullptr);
+ Q_ASSERT(node != firstExpiringNode && node != lastExpiringNode);
+ Q_ASSERT(node->previous == nullptr && node->next == nullptr);
Q_ASSERT(node->useCount == 0);
- if (newest) {
- Q_ASSERT(newest->newer == nullptr);
- newest->newer = node;
- node->older = newest;
- }
- if (!oldest) {
- // there are no entries, so this is the oldest one too
- oldest = node;
+
+ node->timer.setPreciseRemainingTime(node->object->expiryTimeoutSeconds);
+#ifdef DEBUG_ACCESSCACHE
+ qDebug() << "QNetworkAccessCache case trying to insert=" << QString::fromUtf8(key)
+ << node->timer.remainingTime() << "milliseconds";
+ Node *current = lastExpiringNode;
+ while (current) {
+ qDebug() << "QNetworkAccessCache item=" << QString::fromUtf8(current->key)
+ << current->timer.remainingTime() << "milliseconds"
+ << (current == lastExpiringNode ? "[last to expire]" : "")
+ << (current == firstExpiringNode ? "[first to expire]" : "");
+ current = current->previous;
}
+#endif
- node->timestamp = QDateTime::currentDateTimeUtc().addSecs(ExpiryTime);
- newest = node;
+ if (lastExpiringNode) {
+ Q_ASSERT(lastExpiringNode->next == nullptr);
+ if (lastExpiringNode->timer < node->timer) {
+ // Insert as new last-to-expire node.
+ node->previous = lastExpiringNode;
+ lastExpiringNode->next = node;
+ lastExpiringNode = node;
+ } else {
+ // Insert in a sorted way, as different nodes might have had different expiryTimeoutSeconds set.
+ Node *current = lastExpiringNode;
+ while (current->previous != nullptr && current->previous->timer >= node->timer)
+ current = current->previous;
+ node->previous = current->previous;
+ if (node->previous)
+ node->previous->next = node;
+ node->next = current;
+ current->previous = node;
+ if (node->previous == nullptr)
+ firstExpiringNode = node;
+ }
+ } else {
+ // no current last-to-expire node
+ lastExpiringNode = node;
+ }
+ if (!firstExpiringNode) {
+ // there are no entries, so this is the next-to-expire too
+ firstExpiringNode = node;
+ }
+ Q_ASSERT(firstExpiringNode->previous == nullptr);
+ Q_ASSERT(lastExpiringNode->next == nullptr);
}
/*!
Removes the entry pointed by \a key from the linked list.
- Returns \c true if the entry removed was the oldest one.
+ Returns \c true if the entry removed was the next to expire.
*/
bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end())
+ Node * const node = hash.value(key);
+ if (!node)
return false;
- Node *const node = &it.value();
-
- bool wasOldest = false;
- if (node == oldest) {
- oldest = node->newer;
- wasOldest = true;
+ bool wasFirst = false;
+ if (node == firstExpiringNode) {
+ firstExpiringNode = node->next;
+ wasFirst = true;
}
- if (node == newest)
- newest = node->older;
- if (node->older)
- node->older->newer = node->newer;
- if (node->newer)
- node->newer->older = node->older;
-
- node->newer = node->older = nullptr;
- return wasOldest;
+ if (node == lastExpiringNode)
+ lastExpiringNode = node->previous;
+ if (node->previous)
+ node->previous->next = node->next;
+ if (node->next)
+ node->next->previous = node->previous;
+
+ node->next = node->previous = nullptr;
+ return wasFirst;
}
void QNetworkAccessCache::updateTimer()
{
timer.stop();
- if (!oldest)
+ if (!firstExpiringNode)
return;
- int interval = QDateTime::currentDateTimeUtc().secsTo(oldest->timestamp);
+ qint64 interval = firstExpiringNode->timer.remainingTime();
if (interval <= 0) {
interval = 0;
- } else {
- // round up the interval
- interval = (interval + 15) & ~16;
}
- timer.start(interval * 1000, this);
+ // Plus 10 msec so we don't spam timer events if date comparisons are too fuzzy.
+ // This code used to do (broken) rounding, but for ConnectionCacheExpiryTimeoutSecondsAttribute
+ // to work we cannot do this.
+ // See discussion in https://codereview.qt-project.org/c/qt/qtbase/+/337464
+ timer.start(interval + 10, this);
}
bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member)
@@ -222,43 +211,51 @@ bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char
void QNetworkAccessCache::timerEvent(QTimerEvent *)
{
- // expire old items
- const QDateTime now = QDateTime::currentDateTimeUtc();
-
- while (oldest && oldest->timestamp < now) {
- Node *next = oldest->newer;
- oldest->object->dispose();
-
- hash.remove(oldest->key); // oldest gets deleted
- oldest = next;
+ while (firstExpiringNode && firstExpiringNode->timer.hasExpired()) {
+ Node *next = firstExpiringNode->next;
+ firstExpiringNode->object->dispose();
+ hash.remove(firstExpiringNode->key); // `firstExpiringNode` gets deleted
+ delete firstExpiringNode;
+ firstExpiringNode = next;
}
// fixup the list
- if (oldest)
- oldest->older = nullptr;
+ if (firstExpiringNode)
+ firstExpiringNode->previous = nullptr;
else
- newest = nullptr;
+ lastExpiringNode = nullptr;
updateTimer();
}
-void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry)
+void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry, qint64 connectionCacheExpiryTimeoutSeconds)
{
Q_ASSERT(!key.isEmpty());
if (unlinkEntry(key))
updateTimer();
- Node &node = hash[key]; // create the entry in the hash if it didn't exist
- if (node.useCount)
- qWarning("QNetworkAccessCache::addEntry: overriding active cache entry '%s'",
- key.constData());
- if (node.object)
- node.object->dispose();
- node.object = entry;
- node.object->key = key;
- node.key = key;
- node.useCount = 1;
+ Node *node = hash.value(key);
+ if (!node) {
+ node = new Node;
+ hash.insert(key, node);
+ }
+
+ if (node->useCount)
+ qWarning("QNetworkAccessCache::addEntry: overriding active cache entry '%s'", key.constData());
+ if (node->object)
+ node->object->dispose();
+ node->object = entry;
+ node->object->key = key;
+ if (connectionCacheExpiryTimeoutSeconds > -1) {
+ node->object->expiryTimeoutSeconds = connectionCacheExpiryTimeoutSeconds; // via ConnectionCacheExpiryTimeoutSecondsAttribute
+ } else {
+ node->object->expiryTimeoutSeconds = ExpiryTime;
+ }
+ node->key = key;
+ node->useCount = 1;
+
+ // It gets only put into the expiry list in linkEntry (from releaseEntry), when it is not used anymore.
}
bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
@@ -266,41 +263,16 @@ bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
return hash.contains(key);
}
-bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, const char *member)
-{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end())
- return false; // no such entry
-
- Node *node = &it.value();
-
- if (node->useCount > 0 && !node->object->shareable) {
- // object is not shareable and is in use
- // queue for later use
- Q_ASSERT(node->older == nullptr && node->newer == nullptr);
- node->receiverQueue.push_back({target, member});
-
- // request queued
- return true;
- } else {
- // node not in use or is shareable
- if (unlinkEntry(key))
- updateTimer();
-
- ++node->useCount;
- return emitEntryReady(node, target, member);
- }
-}
-
QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const QByteArray &key)
{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end())
+ Node *node = hash.value(key);
+ if (!node)
return nullptr;
- if (it->useCount > 0) {
- if (it->object->shareable) {
- ++it->useCount;
- return it->object;
+
+ if (node->useCount > 0) {
+ if (node->object->shareable) {
+ ++node->useCount;
+ return node->object;
}
// object in use and not shareable
@@ -308,62 +280,42 @@ QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const
}
// entry not in use, let the caller have it
- bool wasOldest = unlinkEntry(key);
- ++it->useCount;
+ bool wasNext = unlinkEntry(key);
+ ++node->useCount;
- if (wasOldest)
+ if (wasNext)
updateTimer();
- return it->object;
+ return node->object;
}
void QNetworkAccessCache::releaseEntry(const QByteArray &key)
{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end()) {
- qWarning("QNetworkAccessCache::releaseEntry: trying to release key '%s' that is not in cache",
- key.constData());
+ Node *node = hash.value(key);
+ if (!node) {
+ qWarning("QNetworkAccessCache::releaseEntry: trying to release key '%s' that is not in cache", key.constData());
return;
}
- Node *node = &it.value();
Q_ASSERT(node->useCount > 0);
- // are there other objects waiting?
- const auto objectStillExists = [](const Receiver &r) { return !r.object.isNull(); };
-
- auto &queue = node->receiverQueue;
- auto qit = std::find_if(queue.begin(), queue.end(), objectStillExists);
-
- const Receiver receiver = qit == queue.end() ? Receiver{} : std::move(*qit++) ;
-
- queue.erase(queue.begin(), qit);
-
- if (receiver.object) {
- // queue another activation
- emitEntryReady(node, receiver.object, receiver.member);
- return;
- }
-
if (!--node->useCount) {
// no objects waiting; add it back to the expiry list
if (node->object->expires)
linkEntry(key);
- if (oldest == node)
+ if (firstExpiringNode == node)
updateTimer();
}
}
void QNetworkAccessCache::removeEntry(const QByteArray &key)
{
- NodeHash::Iterator it = hash.find(key);
- if (it == hash.end()) {
- qWarning("QNetworkAccessCache::removeEntry: trying to remove key '%s' that is not in cache",
- key.constData());
+ Node *node = hash.value(key);
+ if (!node) {
+ qWarning("QNetworkAccessCache::removeEntry: trying to remove key '%s' that is not in cache", key.constData());
return;
}
- Node *node = &it.value();
if (unlinkEntry(key))
updateTimer();
if (node->useCount > 1)
@@ -372,6 +324,9 @@ void QNetworkAccessCache::removeEntry(const QByteArray &key)
node->object->key.clear();
hash.remove(node->key);
+ delete node;
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccesscache_p.cpp"
diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h
index 69ea649a8a..3be7967ca1 100644
--- a/src/network/access/qnetworkaccesscache_p.h
+++ b/src/network/access/qnetworkaccesscache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSCACHE_P_H
#define QNETWORKACCESSCACHE_P_H
@@ -71,7 +35,7 @@ class QNetworkAccessCache: public QObject
Q_OBJECT
public:
struct Node;
- typedef QHash<QByteArray, Node> NodeHash;
+ typedef QHash<QByteArray, Node *> NodeHash;
class CacheableObject
{
@@ -79,6 +43,7 @@ public:
QByteArray key;
bool expires;
bool shareable;
+ qint64 expiryTimeoutSeconds;
public:
CacheableObject();
virtual ~CacheableObject();
@@ -90,14 +55,12 @@ public:
void setShareable(bool enable);
};
- QNetworkAccessCache();
~QNetworkAccessCache();
void clear();
- void addEntry(const QByteArray &key, CacheableObject *entry);
+ void addEntry(const QByteArray &key, CacheableObject *entry, qint64 connectionCacheExpiryTimeoutSeconds = -1);
bool hasEntry(const QByteArray &key) const;
- bool requestEntry(const QByteArray &key, QObject *target, const char *member);
CacheableObject *requestEntryNow(const QByteArray &key);
void releaseEntry(const QByteArray &key);
void removeEntry(const QByteArray &key);
@@ -111,8 +74,8 @@ protected:
private:
// idea copied from qcache.h
NodeHash hash;
- Node *oldest;
- Node *newest;
+ Node *firstExpiringNode = nullptr;
+ Node *lastExpiringNode = nullptr;
QBasicTimer timer;
@@ -124,6 +87,4 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkAccessCache::CacheableObject*)
-
#endif
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 22fdc5bb0b..ead5fe2ef5 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -1,57 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKACCESSCACHEBACKEND_DEBUG
#include "qnetworkaccesscachebackend_p.h"
#include "qabstractnetworkcache.h"
#include "qfileinfo.h"
-#if QT_CONFIG(ftp)
-#include "qurlinfo_p.h"
-#endif
#include "qdir.h"
#include "qcoreapplication.h"
+#include "qhash.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
- : QNetworkAccessBackend()
+ : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local)
{
}
@@ -83,21 +47,21 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
return false;
QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
- setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
-
- // set the raw headers
- const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
- for (const auto &header : rawHeaders) {
- if (header.first.toLower() == "cache-control") {
- const QByteArray cacheControlValue = header.second.toLower();
- if (cacheControlValue.contains("must-revalidate")
- || cacheControlValue.contains("no-cache")) {
- return false;
- }
- }
- setRawHeader(header.first, header.second);
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute,
+ attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute,
+ attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
+
+ // set the headers
+ auto headers = item.headers();
+ const auto cacheControlValue = QLatin1StringView(
+ headers.value(QHttpHeaders::WellKnownHeader::CacheControl));
+ // RFC 9111 Section 5.2 Cache Control
+ if (cacheControlValue.contains("must-revalidate"_L1, Qt::CaseInsensitive)
+ || cacheControlValue.contains("no-cache"_L1, Qt::CaseInsensitive)) {
+ return false;
}
+ setHeaders(std::move(headers));
// handle a possible redirect
QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
@@ -110,11 +74,11 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
metaDataChanged();
if (operation() == QNetworkAccessManager::GetOperation) {
- QIODevice *contents = nc->data(url());
- if (!contents)
+ device = nc->data(url());
+ if (!device)
return false;
- contents->setParent(this);
- writeDownstreamData(contents);
+ device->setParent(this);
+ readyRead();
}
#if defined(QNETWORKACCESSCACHEBACKEND_DEBUG)
@@ -123,23 +87,22 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
return true;
}
-void QNetworkAccessCacheBackend::closeDownstreamChannel()
+bool QNetworkAccessCacheBackend::start()
{
+ open();
+ return true;
}
-void QNetworkAccessCacheBackend::closeUpstreamChannel()
-{
- Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
-}
+void QNetworkAccessCacheBackend::close() { }
-void QNetworkAccessCacheBackend::upstreamReadyRead()
+qint64 QNetworkAccessCacheBackend::bytesAvailable() const
{
- Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+ return device ? device->bytesAvailable() : qint64(0);
}
-void QNetworkAccessCacheBackend::downstreamReadyWrite()
+qint64 QNetworkAccessCacheBackend::read(char *data, qint64 maxlen)
{
- Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+ return device ? device->read(data, maxlen) : qint64(0);
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h
index dfb0ce84d9..80ec1535de 100644
--- a/src/network/access/qnetworkaccesscachebackend_p.h
+++ b/src/network/access/qnetworkaccesscachebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSCACHEBACKEND_P_H
#define QNETWORKACCESSCACHEBACKEND_P_H
@@ -66,15 +30,15 @@ public:
~QNetworkAccessCacheBackend();
void open() override;
- void closeDownstreamChannel() override;
- void closeUpstreamChannel();
-
- void upstreamReadyRead();
- void downstreamReadyWrite() override;
+ void close() override;
+ bool start() override;
+ qint64 bytesAvailable() const override;
+ qint64 read(char *data, qint64 maxlen) override;
private:
bool sendCacheContents();
+ QIODevice *device = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index 03ffc69628..e0a89e3646 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessdebugpipebackend_p.h"
#include "QtCore/qdatastream.h"
@@ -46,6 +10,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#ifdef QT_BUILD_INTERNAL
enum {
@@ -74,14 +40,19 @@ QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation o
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("debugpipe"))
+ if (url.scheme() == "debugpipe"_L1)
return new QNetworkAccessDebugPipeBackend;
return nullptr;
}
QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
- : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
- hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
+ : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Networked),
+ bareProtocol(false),
+ hasUploadFinished(false),
+ hasDownloadFinished(false),
+ hasEverythingFinished(false),
+ bytesDownloaded(0),
+ bytesUploaded(0)
{
}
@@ -98,75 +69,58 @@ void QNetworkAccessDebugPipeBackend::open()
// socket ready read -> we can push from socket to downstream
connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
- connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError()));
+ connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), SLOT(socketError()));
connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
// socket bytes written -> we can push more from upstream to socket
connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
- bareProtocol = QUrlQuery(url()).queryItemValue(QLatin1String("bare")) == QLatin1String("1");
+ bareProtocol = QUrlQuery(url()).queryItemValue("bare"_L1) == "1"_L1;
if (operation() == QNetworkAccessManager::PutOperation) {
createUploadByteDevice();
- QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
+ QObject::connect(uploadByteDevice(), SIGNAL(readyRead()), this,
+ SLOT(uploadReadyReadSlot()));
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
}
}
void QNetworkAccessDebugPipeBackend::socketReadyRead()
{
- pushFromSocketToDownstream();
+ readyRead();
}
-void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
+qint64 QNetworkAccessDebugPipeBackend::read(char *data, qint64 maxlen)
{
- pushFromSocketToDownstream();
+ qint64 haveRead = socket.read(data, maxlen);
+
+ if (haveRead == -1) {
+ hasDownloadFinished = true;
+ // this ensures a good last downloadProgress is emitted
+ auto h = headers();
+ h.removeAll(QHttpHeaders::WellKnownHeader::ContentLength);
+ setHeaders(std::move(h));
+ possiblyFinish();
+ return haveRead;
+ }
+
+ bytesDownloaded += haveRead;
+ return haveRead;
}
-void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
+qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
{
- pushFromUpstreamToSocket();
+ return socket.bytesAvailable();
}
-void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
+void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
{
pushFromUpstreamToSocket();
}
-void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
+void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
{
- QByteArray buffer;
-
- if (socket.state() == QAbstractSocket::ConnectingState) {
- return;
- }
-
- forever {
- if (hasDownloadFinished)
- return;
-
- buffer.resize(ReadBufferSize);
- qint64 haveRead = socket.read(buffer.data(), ReadBufferSize);
-
- if (haveRead == -1) {
- hasDownloadFinished = true;
- // this ensures a good last downloadProgress is emitted
- setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
- possiblyFinish();
- break;
- } else if (haveRead == 0) {
- break;
- } else {
- // have read something
- buffer.resize(haveRead);
- bytesDownloaded += haveRead;
-
- QByteDataBuffer list;
- list.append(buffer);
- buffer.clear(); // important because of implicit sharing!
- writeDownstreamData(list);
- }
- }
+ pushFromUpstreamToSocket();
}
void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
@@ -180,20 +134,20 @@ void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
if (socket.bytesToWrite() >= WriteBufferSize)
return;
- qint64 haveRead;
- const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead);
+ QByteArray data(WriteBufferSize, Qt::Uninitialized);
+ qint64 haveRead = uploadByteDevice()->peek(data.data(), data.size());
if (haveRead == -1) {
// EOF
hasUploadFinished = true;
- emitReplyUploadProgress(bytesUploaded, bytesUploaded);
possiblyFinish();
break;
- } else if (haveRead == 0 || readPointer == nullptr) {
+ } else if (haveRead == 0) {
// nothing to read right now, we will be called again later
break;
} else {
qint64 haveWritten;
- haveWritten = socket.write(readPointer, haveRead);
+ data.truncate(haveRead);
+ haveWritten = socket.write(std::move(data));
if (haveWritten < 0) {
// write error!
@@ -203,13 +157,11 @@ void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
finished();
return;
} else {
- uploadByteDevice->advanceReadPointer(haveWritten);
+ uploadByteDevice()->skip(haveWritten);
bytesUploaded += haveWritten;
- emitReplyUploadProgress(bytesUploaded, -1);
}
//QCoreApplication::processEvents();
-
}
}
}
@@ -232,9 +184,9 @@ void QNetworkAccessDebugPipeBackend::possiblyFinish()
}
-void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
+void QNetworkAccessDebugPipeBackend::close()
{
- qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
+ qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());
//if (operation() == QNetworkAccessManager::GetOperation)
// socket.disconnectFromHost();
}
@@ -266,11 +218,10 @@ void QNetworkAccessDebugPipeBackend::socketError()
void QNetworkAccessDebugPipeBackend::socketDisconnected()
{
- pushFromSocketToDownstream();
-
if (socket.bytesToWrite() == 0) {
// normal close
} else {
+ readyRead(); // @todo this is odd
// abnormal close
QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1")
.arg(url().toString());
@@ -287,3 +238,5 @@ void QNetworkAccessDebugPipeBackend::socketConnected()
#endif
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessdebugpipebackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
index 761c7055b8..b3e9e1d40b 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend_p.h
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSDEBUGPIPEBACKEND_P_H
#define QNETWORKACCESSDEBUGPIPEBACKEND_P_H
@@ -68,13 +32,13 @@ public:
QNetworkAccessDebugPipeBackend();
virtual ~QNetworkAccessDebugPipeBackend();
- virtual void open() override;
- virtual void closeDownstreamChannel() override;
+ void open() override;
+ void close() override;
- virtual void downstreamReadyWrite() override;
+ qint64 read(char *data, qint64 maxlen) override;
+ qint64 bytesAvailable() const override;
protected:
- void pushFromSocketToDownstream();
void pushFromUpstreamToSocket();
void possiblyFinish();
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 507417f86c..2100c188a5 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -1,47 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessfilebackend_p.h"
#include "qfileinfo.h"
-#if QT_CONFIG(ftp)
-#include "qurlinfo_p.h"
-#endif
#include "qdir.h"
#include "private/qnoncontiguousbytedevice_p.h"
@@ -50,6 +11,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const
{
QStringList schemes;
@@ -77,13 +40,13 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0
+ if (url.scheme().compare("qrc"_L1, Qt::CaseInsensitive) == 0
#if defined(Q_OS_ANDROID)
- || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0
+ || url.scheme().compare("assets"_L1, Qt::CaseInsensitive) == 0
#endif
|| url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().length() > 1)) {
+ } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().size() > 1)) {
// check if QFile could, in theory, open this URL via the file engines
// it has to be in the format:
// prefix:path/to/file
@@ -98,8 +61,12 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
return nullptr;
}
+// We pass TargetType::Local even though it's kind of Networked but we're using a QFile to access
+// the resource so it cannot use proxies anyway
QNetworkAccessFileBackend::QNetworkAccessFileBackend()
- : totalBytes(0), hasUploadFinished(false)
+ : QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local),
+ totalBytes(0),
+ hasUploadFinished(false)
{
}
@@ -111,7 +78,7 @@ void QNetworkAccessFileBackend::open()
{
QUrl url = this->url();
- if (url.host() == QLatin1String("localhost"))
+ if (url.host() == "localhost"_L1)
url.setHost(QString());
#if !defined(Q_OS_WIN)
// do not allow UNC paths on Unix
@@ -124,17 +91,17 @@ void QNetworkAccessFileBackend::open()
}
#endif // !defined(Q_OS_WIN)
if (url.path().isEmpty())
- url.setPath(QLatin1String("/"));
+ url.setPath("/"_L1);
setUrl(url);
QString fileName = url.toLocalFile();
if (fileName.isEmpty()) {
- if (url.scheme() == QLatin1String("qrc")) {
- fileName = QLatin1Char(':') + url.path();
+ if (url.scheme() == "qrc"_L1) {
+ fileName = u':' + url.path();
} else {
#if defined(Q_OS_ANDROID)
- if (url.scheme() == QLatin1String("assets"))
- fileName = QLatin1String("assets:") + url.path();
+ if (url.scheme() == "assets"_L1)
+ fileName = "assets:"_L1 + url.path();
else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
@@ -155,7 +122,7 @@ void QNetworkAccessFileBackend::open()
case QNetworkAccessManager::PutOperation:
mode = QIODevice::WriteOnly | QIODevice::Truncate;
createUploadByteDevice();
- QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
+ QObject::connect(uploadByteDevice(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
break;
default:
@@ -166,6 +133,8 @@ void QNetworkAccessFileBackend::open()
mode |= QIODevice::Unbuffered;
bool opened = file.open(mode);
+ if (file.isSequential())
+ connect(&file, &QIODevice::readChannelFinished, this, [this]() { finished(); });
// could we open the file?
if (!opened) {
@@ -189,8 +158,8 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
return;
forever {
- qint64 haveRead;
- const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
+ QByteArray data(16 * 1024, Qt::Uninitialized);
+ qint64 haveRead = uploadByteDevice()->peek(data.data(), data.size());
if (haveRead == -1) {
// EOF
hasUploadFinished = true;
@@ -198,12 +167,13 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
file.close();
finished();
break;
- } else if (haveRead == 0 || readPointer == nullptr) {
+ } else if (haveRead == 0) {
// nothing to read right now, we will be called again later
break;
} else {
qint64 haveWritten;
- haveWritten = file.write(readPointer, haveRead);
+ data.truncate(haveRead);
+ haveWritten = file.write(data);
if (haveWritten < 0) {
// write error!
@@ -214,7 +184,7 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
finished();
return;
} else {
- uploadByteDevice->advanceReadPointer(haveWritten);
+ uploadByteDevice()->skip(haveWritten);
}
@@ -223,21 +193,13 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
}
}
-void QNetworkAccessFileBackend::closeDownstreamChannel()
+void QNetworkAccessFileBackend::close()
{
if (operation() == QNetworkAccessManager::GetOperation) {
file.close();
}
}
-void QNetworkAccessFileBackend::downstreamReadyWrite()
-{
- Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
- "We're being told to download data but operation isn't GET!");
-
- readMoreFromFile();
-}
-
bool QNetworkAccessFileBackend::loadFileInfo()
{
QFileInfo fi(file);
@@ -257,40 +219,38 @@ bool QNetworkAccessFileBackend::loadFileInfo()
return true;
}
-bool QNetworkAccessFileBackend::readMoreFromFile()
+qint64 QNetworkAccessFileBackend::bytesAvailable() const
{
- qint64 wantToRead;
- while ((wantToRead = nextDownstreamBlockSize()) > 0) {
- // ### FIXME!!
- // Obtain a pointer from the ringbuffer!
- // Avoid extra copy
- QByteArray data;
- data.reserve(wantToRead);
- qint64 actuallyRead = file.read(data.data(), wantToRead);
- if (actuallyRead <= 0) {
- // EOF or error
- if (file.error() != QFile::NoError) {
- QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
- .arg(url().toString(), file.errorString());
- error(QNetworkReply::ProtocolFailure, msg);
+ if (operation() != QNetworkAccessManager::GetOperation)
+ return 0;
+ return file.bytesAvailable();
+}
- finished();
- return false;
- }
+qint64 QNetworkAccessFileBackend::read(char *data, qint64 maxlen)
+{
+ if (operation() != QNetworkAccessManager::GetOperation)
+ return 0;
+ qint64 actuallyRead = file.read(data, maxlen);
+ if (actuallyRead <= 0) {
+ // EOF or error
+ if (file.error() != QFile::NoError) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
+ .arg(url().toString(), file.errorString());
+ error(QNetworkReply::ProtocolFailure, msg);
finished();
- return true;
+ return -1;
}
- data.resize(actuallyRead);
- totalBytes += actuallyRead;
-
- QByteDataBuffer list;
- list.append(data);
- data.clear(); // important because of implicit sharing!
- writeDownstreamData(list);
+ finished();
+ return actuallyRead;
}
- return true;
+ if (!file.isSequential() && file.atEnd())
+ finished();
+ totalBytes += actuallyRead;
+ return actuallyRead;
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessfilebackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
index 2204958ee0..2c3a106f2a 100644
--- a/src/network/access/qnetworkaccessfilebackend_p.h
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSFILEBACKEND_P_H
#define QNETWORKACCESSFILEBACKEND_P_H
@@ -66,10 +30,11 @@ public:
QNetworkAccessFileBackend();
virtual ~QNetworkAccessFileBackend();
- virtual void open() override;
- virtual void closeDownstreamChannel() override;
+ void open() override;
+ void close() override;
- virtual void downstreamReadyWrite() override;
+ qint64 bytesAvailable() const override;
+ qint64 read(char *data, qint64 maxlen) override;
public slots:
void uploadReadyReadSlot();
@@ -79,7 +44,6 @@ private:
bool hasUploadFinished;
bool loadFileInfo();
- bool readMoreFromFile();
};
class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
deleted file mode 100644
index fb8cd79c12..0000000000
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ /dev/null
@@ -1,440 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkaccessftpbackend_p.h"
-#include "qnetworkaccessmanager_p.h"
-#include "QtNetwork/qauthenticator.h"
-#include "private/qnoncontiguousbytedevice_p.h"
-#include <QStringList>
-
-QT_BEGIN_NAMESPACE
-
-enum {
- DefaultFtpPort = 21
-};
-
-static QByteArray makeCacheKey(const QUrl &url)
-{
- QUrl copy = url;
- copy.setPort(url.port(DefaultFtpPort));
- return "ftp-connection:" +
- copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery |
- QUrl::RemoveFragment);
-}
-
-QStringList QNetworkAccessFtpBackendFactory::supportedSchemes() const
-{
- return QStringList(QStringLiteral("ftp"));
-}
-
-QNetworkAccessBackend *
-QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
- const QNetworkRequest &request) const
-{
- // is it an operation we know of?
- switch (op) {
- case QNetworkAccessManager::GetOperation:
- case QNetworkAccessManager::PutOperation:
- break;
-
- default:
- // no, we can't handle this operation
- return nullptr;
- }
-
- QUrl url = request.url();
- if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
- return new QNetworkAccessFtpBackend;
- return nullptr;
-}
-
-class QNetworkAccessCachedFtpConnection: public QFtp, public QNetworkAccessCache::CacheableObject
-{
- // Q_OBJECT
-public:
- QNetworkAccessCachedFtpConnection()
- {
- setExpires(true);
- setShareable(false);
- }
-
- void dispose() override
- {
- connect(this, SIGNAL(done(bool)), this, SLOT(deleteLater()));
- close();
- }
-
- using QFtp::clearError;
-};
-
-QNetworkAccessFtpBackend::QNetworkAccessFtpBackend()
- : ftp(nullptr), uploadDevice(nullptr), totalBytes(0), helpId(-1), sizeId(-1), mdtmId(-1), pwdId(-1),
- supportsSize(false), supportsMdtm(false), supportsPwd(false), state(Idle)
-{
-}
-
-QNetworkAccessFtpBackend::~QNetworkAccessFtpBackend()
-{
- //if backend destroyed while in use, then abort (this is the code path from QNetworkReply::abort)
- if (ftp && state != Disconnecting)
- ftp->abort();
- disconnectFromFtp(RemoveCachedConnection);
-}
-
-void QNetworkAccessFtpBackend::open()
-{
-#ifndef QT_NO_NETWORKPROXY
- QNetworkProxy proxy;
- const auto proxies = proxyList();
- for (const QNetworkProxy &p : proxies) {
- // use the first FTP proxy
- // or no proxy at all
- if (p.type() == QNetworkProxy::FtpCachingProxy
- || p.type() == QNetworkProxy::NoProxy) {
- proxy = p;
- break;
- }
- }
-
- // did we find an FTP proxy or a NoProxy?
- if (proxy.type() == QNetworkProxy::DefaultProxy) {
- // unsuitable proxies
- error(QNetworkReply::ProxyNotFoundError,
- tr("No suitable proxy found"));
- finished();
- return;
- }
-
-#endif
-
- QUrl url = this->url();
- if (url.path().isEmpty()) {
- url.setPath(QLatin1String("/"));
- setUrl(url);
- }
- if (url.path().endsWith(QLatin1Char('/'))) {
- error(QNetworkReply::ContentOperationNotPermittedError,
- tr("Cannot open %1: is a directory").arg(url.toString()));
- finished();
- return;
- }
- state = LoggingIn;
-
- QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this);
- QByteArray cacheKey = makeCacheKey(url);
- if (!objectCache->requestEntry(cacheKey, this,
- SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
- ftp = new QNetworkAccessCachedFtpConnection;
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the QFtp
- ftp->setProperty("_q_networksession", property("_q_networksession"));
-#endif
-#ifndef QT_NO_NETWORKPROXY
- if (proxy.type() == QNetworkProxy::FtpCachingProxy)
- ftp->setProxy(proxy.hostName(), proxy.port());
-#endif
- ftp->connectToHost(url.host(), url.port(DefaultFtpPort));
- ftp->login(url.userName(), url.password());
-
- objectCache->addEntry(cacheKey, ftp);
- ftpConnectionReady(ftp);
- }
-
- // Put operation
- if (operation() == QNetworkAccessManager::PutOperation) {
- uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice());
- uploadDevice->setParent(this);
- }
-}
-
-void QNetworkAccessFtpBackend::closeDownstreamChannel()
-{
- state = Disconnecting;
- if (operation() == QNetworkAccessManager::GetOperation)
- ftp->abort();
-}
-
-void QNetworkAccessFtpBackend::downstreamReadyWrite()
-{
- if (state == Transferring && ftp && ftp->bytesAvailable())
- ftpReadyRead();
-}
-
-void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o)
-{
- ftp = static_cast<QNetworkAccessCachedFtpConnection *>(o);
- connect(ftp, SIGNAL(done(bool)), SLOT(ftpDone()));
- connect(ftp, SIGNAL(rawCommandReply(int,QString)), SLOT(ftpRawCommandReply(int,QString)));
- connect(ftp, SIGNAL(readyRead()), SLOT(ftpReadyRead()));
-
- // is the login process done already?
- if (ftp->state() == QFtp::LoggedIn)
- ftpDone();
-
- // no, defer the actual operation until after we've logged in
-}
-
-void QNetworkAccessFtpBackend::disconnectFromFtp(CacheCleanupMode mode)
-{
- state = Disconnecting;
-
- if (ftp) {
- disconnect(ftp, nullptr, this, nullptr);
-
- QByteArray key = makeCacheKey(url());
- if (mode == RemoveCachedConnection) {
- QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key);
- ftp->dispose();
- } else {
- QNetworkAccessManagerPrivate::getObjectCache(this)->releaseEntry(key);
- }
-
- ftp = nullptr;
- }
-}
-
-void QNetworkAccessFtpBackend::ftpDone()
-{
- // the last command we sent is done
- if (state == LoggingIn && ftp->state() != QFtp::LoggedIn) {
- if (ftp->state() == QFtp::Connected) {
- // the login did not succeed
- QUrl newUrl = url();
- QString userInfo = newUrl.userInfo();
- newUrl.setUserInfo(QString());
- setUrl(newUrl);
-
- QAuthenticator auth;
- authenticationRequired(&auth);
-
- if (!auth.isNull()) {
- // try again:
- newUrl.setUserName(auth.user());
- ftp->login(auth.user(), auth.password());
- return;
- }
-
- // Re insert the user info so that we can remove the cache entry.
- newUrl.setUserInfo(userInfo);
- setUrl(newUrl);
-
- error(QNetworkReply::AuthenticationRequiredError,
- tr("Logging in to %1 failed: authentication required")
- .arg(url().host()));
- } else {
- // we did not connect
- QNetworkReply::NetworkError code;
- switch (ftp->error()) {
- case QFtp::HostNotFound:
- code = QNetworkReply::HostNotFoundError;
- break;
-
- case QFtp::ConnectionRefused:
- code = QNetworkReply::ConnectionRefusedError;
- break;
-
- default:
- code = QNetworkReply::ProtocolFailure;
- break;
- }
-
- error(code, ftp->errorString());
- }
-
- // we're not connected, so remove the cache entry:
- disconnectFromFtp(RemoveCachedConnection);
- finished();
- return;
- }
-
- // check for errors:
- if (state == CheckingFeatures && ftp->error() == QFtp::UnknownError) {
- qWarning("QNetworkAccessFtpBackend: HELP command failed, ignoring it");
- ftp->clearError();
- } else if (ftp->error() != QFtp::NoError) {
- QString msg;
- if (operation() == QNetworkAccessManager::GetOperation)
- msg = tr("Error while downloading %1: %2");
- else
- msg = tr("Error while uploading %1: %2");
- msg = msg.arg(url().toString(), ftp->errorString());
-
- if (state == Statting)
- // file probably doesn't exist
- error(QNetworkReply::ContentNotFoundError, msg);
- else
- error(QNetworkReply::ContentAccessDenied, msg);
-
- disconnectFromFtp(RemoveCachedConnection);
- finished();
- }
-
- if (state == LoggingIn) {
- state = CheckingFeatures;
- // send help command to find out if server supports SIZE, MDTM, and PWD
- if (operation() == QNetworkAccessManager::GetOperation
- || operation() == QNetworkAccessManager::PutOperation) {
- helpId = ftp->rawCommand(QLatin1String("HELP")); // get supported commands
- } else {
- ftpDone();
- }
- } else if (state == CheckingFeatures) {
- // If a URL path starts with // prefix (/%2F decoded), the resource will
- // be retrieved by an absolute path starting with the root directory.
- // For the other URLs, the working directory is retrieved by PWD command
- // and prepended to the resource path as an absolute path starting with
- // the working directory.
- state = ResolvingPath;
- QString path = url().path();
- if (path.startsWith(QLatin1String("//")) || supportsPwd == false) {
- ftpDone(); // no commands sent, move to the next state
- } else {
- // If a path starts with /~/ prefix, its prefix will be replaced by
- // the working directory as an absolute path starting with working
- // directory.
- if (path.startsWith(QLatin1String("/~/"))) {
- // Remove leading /~ symbols
- QUrl newUrl = url();
- newUrl.setPath(path.mid(2));
- setUrl(newUrl);
- }
-
- // send PWD command to retrieve the working directory
- pwdId = ftp->rawCommand(QLatin1String("PWD"));
- }
- } else if (state == ResolvingPath) {
- state = Statting;
- if (operation() == QNetworkAccessManager::GetOperation) {
- // logged in successfully, send the stat requests (if supported)
- const QString path = url().path();
- if (supportsSize) {
- ftp->rawCommand(QLatin1String("TYPE I"));
- sizeId = ftp->rawCommand(QLatin1String("SIZE ") + path); // get size
- }
- if (supportsMdtm)
- mdtmId = ftp->rawCommand(QLatin1String("MDTM ") + path); // get modified time
- if (!supportsSize && !supportsMdtm)
- ftpDone(); // no commands sent, move to the next state
- } else {
- ftpDone();
- }
- } else if (state == Statting) {
- // statted successfully, send the actual request
- metaDataChanged();
- state = Transferring;
-
- QFtp::TransferType type = QFtp::Binary;
- if (operation() == QNetworkAccessManager::GetOperation) {
- setCachingEnabled(true);
- ftp->get(url().path(), nullptr, type);
- } else {
- ftp->put(uploadDevice, url().path(), type);
- }
-
- } else if (state == Transferring) {
- // upload or download finished
- disconnectFromFtp();
- finished();
- }
-}
-
-void QNetworkAccessFtpBackend::ftpReadyRead()
-{
- QByteArray data = ftp->readAll();
- QByteDataBuffer list;
- list.append(data);
- data.clear(); // important because of implicit sharing!
- writeDownstreamData(list);
-}
-
-void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text)
-{
- //qDebug() << "FTP reply:" << code << text;
- int id = ftp->currentId();
-
- if ((id == helpId) && ((code == 200) || (code == 214))) { // supported commands
- // the "FEAT" ftp command would be nice here, but it is not part of the
- // initial FTP RFC 959, neither ar "SIZE" nor "MDTM" (they are all specified
- // in RFC 3659)
- if (text.contains(QLatin1String("SIZE"), Qt::CaseSensitive))
- supportsSize = true;
- if (text.contains(QLatin1String("MDTM"), Qt::CaseSensitive))
- supportsMdtm = true;
- if (text.contains(QLatin1String("PWD"), Qt::CaseSensitive))
- supportsPwd = true;
- } else if (id == pwdId && code == 257) {
- QString pwdPath;
- int startIndex = text.indexOf('"');
- int stopIndex = text.lastIndexOf('"');
- if (stopIndex - startIndex) {
- // The working directory is a substring between \" symbols.
- startIndex++; // skip the first \" symbol
- pwdPath = text.mid(startIndex, stopIndex - startIndex);
- } else {
- // If there is no or only one \" symbol, use all the characters of
- // text.
- pwdPath = text;
- }
-
- // If a URL path starts with the working directory prefix, its resource
- // will be retrieved from the working directory. Otherwise, the path of
- // the working directory is prepended to the resource path.
- QString urlPath = url().path();
- if (!urlPath.startsWith(pwdPath)) {
- if (pwdPath.endsWith(QLatin1Char('/')))
- pwdPath.chop(1);
- // Prepend working directory to the URL path
- QUrl newUrl = url();
- newUrl.setPath(pwdPath % urlPath);
- setUrl(newUrl);
- }
- } else if (code == 213) { // file status
- if (id == sizeId) {
- // reply to the size command
- setHeader(QNetworkRequest::ContentLengthHeader, text.toLongLong());
-#if QT_CONFIG(datestring)
- } else if (id == mdtmId) {
- QDateTime dt = QDateTime::fromString(text, QLatin1String("yyyyMMddHHmmss"));
- setHeader(QNetworkRequest::LastModifiedHeader, dt);
-#endif
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessftpbackend_p.h b/src/network/access/qnetworkaccessftpbackend_p.h
deleted file mode 100644
index 0b3d35dcd3..0000000000
--- a/src/network/access/qnetworkaccessftpbackend_p.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKACCESSFTPBACKEND_P_H
-#define QNETWORKACCESSFTPBACKEND_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the Network Access API. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworkaccessbackend_p.h"
-#include "qnetworkaccesscache_p.h"
-#include "qnetworkrequest.h"
-#include "qnetworkreply.h"
-#include "private/qftp_p.h"
-
-#include "QtCore/qpointer.h"
-
-QT_REQUIRE_CONFIG(ftp);
-
-QT_BEGIN_NAMESPACE
-
-class QNetworkAccessFtpIODevice;
-class QNetworkAccessCachedFtpConnection;
-
-class QNetworkAccessFtpBackend: public QNetworkAccessBackend
-{
- Q_OBJECT
-public:
- enum State {
- Idle,
- //Connecting,
- LoggingIn,
- CheckingFeatures,
- ResolvingPath,
- Statting,
- Transferring,
- Disconnecting
- };
-
- QNetworkAccessFtpBackend();
- virtual ~QNetworkAccessFtpBackend();
-
- virtual void open() override;
- virtual void closeDownstreamChannel() override;
-
- virtual void downstreamReadyWrite() override;
-
- enum CacheCleanupMode {
- ReleaseCachedConnection,
- RemoveCachedConnection
- };
-
- void disconnectFromFtp(CacheCleanupMode mode = ReleaseCachedConnection);
-
-public slots:
- void ftpConnectionReady(QNetworkAccessCache::CacheableObject *object);
- void ftpDone();
- void ftpReadyRead();
- void ftpRawCommandReply(int code, const QString &text);
-
-private:
- friend class QNetworkAccessFtpIODevice;
- QPointer<QNetworkAccessCachedFtpConnection> ftp;
- QIODevice *uploadDevice;
- qint64 totalBytes;
- int helpId, sizeId, mdtmId, pwdId;
- bool supportsSize, supportsMdtm, supportsPwd;
- State state;
-};
-
-class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory
-{
-public:
- virtual QStringList supportedSchemes() const override;
- virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
- const QNetworkRequest &request) const override;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index ff916ff283..ae99721758 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1,41 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessmanager.h"
#include "qnetworkaccessmanager_p.h"
@@ -52,27 +18,23 @@
#include "qhstsstore_p.h"
#endif // QT_CONFIG(settings)
-#include "QtNetwork/qnetworksession.h"
-#include "QtNetwork/private/qsharednetworksession_p.h"
-
-#if QT_CONFIG(ftp)
-#include "qnetworkaccessftpbackend_p.h"
-#endif
#include "qnetworkaccessfilebackend_p.h"
#include "qnetworkaccessdebugpipebackend_p.h"
#include "qnetworkaccesscachebackend_p.h"
#include "qnetworkreplydataimpl_p.h"
#include "qnetworkreplyfileimpl_p.h"
+#include "qnetworkaccessbackend_p.h"
+#include "qnetworkreplyimpl_p.h"
+
#include "QtCore/qbuffer.h"
+#include "QtCore/qlist.h"
#include "QtCore/qurl.h"
-#include "QtCore/qvector.h"
#include "QtNetwork/private/qauthenticator_p.h"
#include "QtNetwork/qsslconfiguration.h"
-#include "QtNetwork/qnetworkconfigmanager.h"
-#include "QtNetwork/private/http2protocol_p.h"
#if QT_CONFIG(http)
+#include "QtNetwork/private/http2protocol_p.h"
#include "qhttpmultipart.h"
#include "qhttpmultipart_p.h"
#include "qnetworkreplyhttpimpl_p.h"
@@ -82,90 +44,117 @@
#include <QHostInfo>
+#include "QtCore/qapplicationstatic.h"
+#include "QtCore/qloggingcategory.h"
+#include <QtCore/private/qfactoryloader_p.h>
+
#if defined(Q_OS_MACOS)
+#include <QtCore/private/qcore_mac_p.h>
+
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SystemConfiguration.h>
-#include <Security/SecKeychain.h>
+#include <Security/Security.h>
#endif
#ifdef Q_OS_WASM
#include "qnetworkreplywasmimpl_p.h"
+#include "qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
#endif
#include "qnetconmonitor_p.h"
+#include <mutex>
+
QT_BEGIN_NAMESPACE
-Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
-#if QT_CONFIG(ftp)
-Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
-#endif // QT_CONFIG(ftp)
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
-#ifdef QT_BUILD_INTERNAL
+Q_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager")
+
+Q_APPLICATION_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
+
+#if QT_CONFIG(private_tests)
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
#endif
-#if defined(Q_OS_MACX)
+Q_APPLICATION_STATIC(QFactoryLoader, qnabfLoader, QNetworkAccessBackendFactory_iid, "/networkaccess"_L1)
+
+#if defined(Q_OS_MACOS)
bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password)
{
- OSStatus err;
- SecKeychainItemRef itemRef;
- bool retValue = false;
- SecProtocolType protocolType = kSecProtocolTypeAny;
- if (scheme.compare(QLatin1String("ftp"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeFTPProxy;
- } else if (scheme.compare(QLatin1String("http"),Qt::CaseInsensitive)==0
- || scheme.compare(QLatin1String("preconnect-http"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeHTTPProxy;
- } else if (scheme.compare(QLatin1String("https"),Qt::CaseInsensitive)==0
- || scheme.compare(QLatin1String("preconnect-https"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeHTTPSProxy;
+ CFStringRef protocolType = nullptr;
+ if (scheme.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolFTPProxy;
+ } else if (scheme.compare("http"_L1, Qt::CaseInsensitive) == 0
+ || scheme.compare("preconnect-http"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolHTTPProxy;
+ } else if (scheme.compare("https"_L1,Qt::CaseInsensitive)==0
+ || scheme.compare("preconnect-https"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolHTTPSProxy;
+ } else {
+ qCWarning(lcQnam) << "Cannot query user name and password for a proxy, unnknown protocol:"
+ << scheme;
+ return false;
}
- QByteArray proxyHostnameUtf8(proxyHostname.toUtf8());
- err = SecKeychainFindInternetPassword(NULL,
- proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(),
- 0,NULL,
- 0, NULL,
- 0, NULL,
- 0,
- protocolType,
- kSecAuthenticationTypeAny,
- 0, NULL,
- &itemRef);
- if (err == noErr) {
-
- SecKeychainAttribute attr;
- SecKeychainAttributeList attrList;
- UInt32 length;
- void *outData;
-
- attr.tag = kSecAccountItemAttr;
- attr.length = 0;
- attr.data = NULL;
-
- attrList.count = 1;
- attrList.attr = &attr;
-
- if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) {
- username = QString::fromUtf8((const char*)attr.data, attr.length);
- password = QString::fromUtf8((const char*)outData, length);
- SecKeychainItemFreeContent(&attrList,outData);
- retValue = true;
- }
- CFRelease(itemRef);
+
+ QCFType<CFMutableDictionaryRef> query(CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0, nullptr, nullptr));
+ Q_ASSERT(query);
+
+ CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
+ CFDictionaryAddValue(query, kSecAttrProtocol, protocolType);
+
+ QCFType<CFStringRef> serverName; // Note the scope.
+ if (proxyHostname.size()) {
+ serverName = proxyHostname.toCFString();
+ CFDictionaryAddValue(query, kSecAttrServer, serverName);
}
- return retValue;
+
+ // This is to get the user name in the result:
+ CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
+ // This one to get the password:
+ CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
+
+ // The default for kSecMatchLimit key is 1 (the first match only), which is fine,
+ // so don't set this value explicitly.
+
+ QCFType<CFTypeRef> replyData;
+ if (SecItemCopyMatching(query, &replyData) != errSecSuccess) {
+ qCWarning(lcQnam, "Failed to extract user name and password from the keychain.");
+ return false;
+ }
+
+ if (!replyData || CFDictionaryGetTypeID() != CFGetTypeID(replyData)) {
+ qCWarning(lcQnam, "Query returned data in unexpected format.");
+ return false;
+ }
+
+ CFDictionaryRef accountData = replyData.as<CFDictionaryRef>();
+ const void *value = CFDictionaryGetValue(accountData, kSecAttrAccount);
+ if (!value || CFGetTypeID(value) != CFStringGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find user name or its format is unknown.");
+ return false;
+ }
+ username = QString::fromCFString(static_cast<CFStringRef>(value));
+
+ value = CFDictionaryGetValue(accountData, kSecValueData);
+ if (!value || CFGetTypeID(value) != CFDataGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find password or its format is unknown.");
+ return false;
+ }
+ const CFDataRef passData = static_cast<const CFDataRef>(value);
+ password = QString::fromLocal8Bit(reinterpret_cast<const char *>(CFDataGetBytePtr(passData)),
+ qsizetype(CFDataGetLength(passData)));
+ return true;
}
-#endif
+#endif // Q_OS_MACOS
static void ensureInitialized()
{
-#if QT_CONFIG(ftp)
- (void) ftpBackend();
-#endif
-
-#ifdef QT_BUILD_INTERNAL
+#if QT_CONFIG(private_tests)
(void) debugpipeBackend();
#endif
@@ -220,27 +209,6 @@ static void ensureInitialized()
can be:
\snippet code/src_network_access_qnetworkaccessmanager.cpp 1
- \section1 Network and Roaming Support
-
- With the addition of the \l {Bearer Management} API to Qt 4.7
- QNetworkAccessManager gained the ability to manage network connections.
- QNetworkAccessManager can start the network interface if the device is
- offline and terminates the interface if the current process is the last
- one to use the uplink. Note that some platforms utilize grace periods from
- when the last application stops using a uplink until the system actually
- terminates the connectivity link. Roaming is equally transparent. Any
- queued/pending network requests are automatically transferred to the new
- access point.
-
- Clients wanting to utilize this feature should not require any changes. In fact
- it is likely that existing platform specific connection code can simply be
- removed from the application.
-
- \note The network and roaming support in QNetworkAccessManager is conditional
- upon the platform supporting connection management. The
- \l QNetworkConfigurationManager::NetworkSessionRequired can be used to
- detect whether QNetworkAccessManager utilizes this feature.
-
\sa QNetworkRequest, QNetworkReply, QNetworkProxy
*/
@@ -273,53 +241,10 @@ static void ensureInitialized()
*/
/*!
- \enum QNetworkAccessManager::NetworkAccessibility
-
- Indicates whether the network is accessible via this network access manager.
-
- \value UnknownAccessibility The network accessibility cannot be determined.
- \value NotAccessible The network is not currently accessible, either because there
- is currently no network coverage or network access has been
- explicitly disabled by a call to setNetworkAccessible().
- \value Accessible The network is accessible.
-
- \sa networkAccessible
-*/
-
-/*!
- \property QNetworkAccessManager::networkAccessible
- \brief whether the network is currently accessible via this network access manager.
-
- \since 4.7
-
- If the network is \l {NotAccessible}{not accessible} the network access manager will not
- process any new network requests, all such requests will fail with an error. Requests with
- URLs with the file:// scheme will still be processed.
-
- By default the value of this property reflects the physical state of the device. Applications
- may override it to disable all network requests via this network access manager by calling
-
- \snippet code/src_network_access_qnetworkaccessmanager.cpp 4
-
- Network requests can be re-enabled again, and this property will resume to
- reflect the actual device state by calling
-
- \snippet code/src_network_access_qnetworkaccessmanager.cpp 5
-
- \note Calling setNetworkAccessible() does not change the network state.
-*/
-
-/*!
- \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
-
- This signal is emitted when the value of the \l networkAccessible property changes.
- \a accessible is the new network accessibility.
-*/
-
-/*!
\fn void QNetworkAccessManager::networkSessionConnected()
\since 4.7
+ \deprecated
\internal
@@ -472,6 +397,7 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
: QObject(*new QNetworkAccessManagerPrivate, parent)
{
ensureInitialized();
+ d_func()->ensureBackendPluginsLoaded();
qRegisterMetaType<QNetworkReply::NetworkError>();
#ifndef QT_NO_NETWORKPROXY
@@ -488,27 +414,6 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
#endif
qRegisterMetaType<QNetworkReply::NetworkError>();
qRegisterMetaType<QSharedPointer<char> >();
-
- Q_D(QNetworkAccessManager);
-
- if (QNetworkStatusMonitor::isEnabled()) {
- connect(&d->statusMonitor, SIGNAL(onlineStateChanged(bool)),
- SLOT(_q_onlineStateChanged(bool)));
-#ifdef QT_NO_BEARERMANAGEMENT
- d->networkAccessible = d->statusMonitor.isNetworkAccessible();
-#else
- d->networkAccessible = d->statusMonitor.isNetworkAccessible() ? Accessible : NotAccessible;
- } else {
- // if a session is required, we track online state through
- // the QNetworkSession's signals if a request is already made.
- // we need to track current accessibility state by default
- //
- connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
- SLOT(_q_onlineStateChanged(bool)));
- connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)),
- SLOT(_q_configurationChanged(QNetworkConfiguration)));
-#endif // QT_NO_BEARERMANAGEMENT
- }
}
/*!
@@ -769,7 +674,7 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const
store is enabled, these policies will be preserved in the store. In case both
cache and store contain the same known hosts, policies from cache are considered
to be more up-to-date (and thus will overwrite the previous values in the store).
- If this behavior is undesired, enable HSTS store before enabling Strict Tranport
+ If this behavior is undesired, enable HSTS store before enabling Strict Transport
Security. By default, the persistent store of HSTS policies is disabled.
\sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(),
@@ -783,7 +688,8 @@ void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, con
d->stsStore.reset(enabled ? new QHstsStore(storeDir) : nullptr);
d->stsCache.setStore(d->stsStore.data());
#else
- Q_UNUSED(enabled) Q_UNUSED(storeDir)
+ Q_UNUSED(enabled);
+ Q_UNUSED(storeDir);
qWarning("HSTS permanent store requires the feature 'settings' enabled");
#endif // QT_CONFIG(settings)
}
@@ -827,7 +733,7 @@ bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const
\sa addStrictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy
*/
-void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts)
+void QNetworkAccessManager::addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts)
{
Q_D(QNetworkAccessManager);
d->stsCache.updateFromPolicies(knownHosts);
@@ -842,7 +748,7 @@ void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsP
\sa addStrictTransportSecurityHosts(), QHstsPolicy
*/
-QVector<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const
+QList<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const
{
Q_D(const QNetworkAccessManager);
return d->stsCache.policies();
@@ -875,6 +781,46 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
}
/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, QIODevice *data)
+{
+ QNetworkRequest newRequest(request);
+ return d_func()->postProcess(
+ createRequest(QNetworkAccessManager::GetOperation, newRequest, data));
+}
+
+/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = get(request, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+/*!
Sends an HTTP POST request to the destination specified by \a request
and returns a new QNetworkReply object opened for reading that will
contain the reply sent by the server. The contents of the \a data
@@ -910,7 +856,25 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
return reply;
}
-#if QT_CONFIG(http)
+/*!
+ \overload
+
+ \since 6.8
+
+ Sends the POST request specified by \a request without a body and returns
+ a new QNetworkReply object.
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, std::nullptr_t nptr)
+{
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
+
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation,
+ request,
+ dev));
+}
+
+#if QT_CONFIG(http) || defined(Q_OS_WASM)
/*!
\since 4.8
@@ -994,192 +958,37 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const
}
/*!
- \since 4.6
-
- Sends a request to delete the resource identified by the URL of \a request.
-
- \note This feature is currently available for HTTP only, performing an
- HTTP DELETE request.
-
- \sa get(), post(), put(), sendCustomRequest()
-*/
-QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
-{
- return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
-}
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-/*!
- \since 4.7
-
- Sets the network configuration that will be used when creating the
- \l {QNetworkSession}{network session} to \a config.
-
- The network configuration is used to create and open a network session before any request that
- requires network access is process. If no network configuration is explicitly set via this
- function the network configuration returned by
- QNetworkConfigurationManager::defaultConfiguration() will be used.
-
- To restore the default network configuration set the network configuration to the value
- returned from QNetworkConfigurationManager::defaultConfiguration().
-
- Setting a network configuration means that the QNetworkAccessManager instance will only
- be using the specified one. In particular, if the default network configuration changes
- (upon e.g. Wifi being available), this new configuration needs to be enabled
- manually if desired.
-
- \snippet code/src_network_access_qnetworkaccessmanager.cpp 2
-
- If an invalid network configuration is set, a network session will not be created. In this
- case network requests will be processed regardless, but may fail. For example:
-
- \snippet code/src_network_access_qnetworkaccessmanager.cpp 3
-
- \sa configuration(), QNetworkSession
-*/
-void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config)
-{
- Q_D(QNetworkAccessManager);
-
- d->networkConfiguration = config;
- d->customNetworkConfiguration = true;
- d->createSession(config);
-}
-
-/*!
- \since 4.7
-
- Returns the network configuration that will be used to create the
- \l {QNetworkSession}{network session} which will be used when processing network requests.
-
- \sa setConfiguration(), activeConfiguration()
-*/
-QNetworkConfiguration QNetworkAccessManager::configuration() const
-{
- Q_D(const QNetworkAccessManager);
-
- QSharedPointer<QNetworkSession> session(d->getNetworkSession());
- if (session && !d->statusMonitor.isEnabled()) {
- return session->configuration();
- } else {
- return d->networkConfigurationManager.defaultConfiguration();
- }
-}
-
-/*!
- \since 4.7
-
- Returns the current active network configuration.
-
- If the network configuration returned by configuration() is of type
- QNetworkConfiguration::ServiceNetwork this function will return the current active child
- network configuration of that configuration. Otherwise returns the same network configuration
- as configuration().
+ \overload
- Use this function to return the actual network configuration currently in use by the network
- session.
+ \since 6.8
- \sa configuration()
+ Sends the PUT request specified by \a request without a body and returns
+ a new QNetworkReply object.
*/
-QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
-{
- Q_D(const QNetworkAccessManager);
-
- QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
- if (networkSession && !d->statusMonitor.isEnabled()) {
- return d->networkConfigurationManager.configurationFromIdentifier(
- networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString());
- } else {
- return d->networkConfigurationManager.defaultConfiguration();
- }
-}
-/*!
- \since 4.7
-
- Overrides the reported network accessibility. If \a accessible is NotAccessible the reported
- network accessiblity will always be NotAccessible. Otherwise the reported network
- accessibility will reflect the actual device state.
-*/
-void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, std::nullptr_t nptr)
{
- Q_D(QNetworkAccessManager);
-
- d->defaultAccessControl = accessible == NotAccessible ? false : true;
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
- if (d->networkAccessible != accessible) {
- NetworkAccessibility previous = networkAccessible();
- d->networkAccessible = accessible;
- NetworkAccessibility current = networkAccessible();
- if (previous != current)
- emit networkAccessibleChanged(current);
- }
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, dev));
}
/*!
- \since 4.7
-
- Returns the current network accessibility.
-*/
-QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
-{
- Q_D(const QNetworkAccessManager);
-
- if (d->statusMonitor.isEnabled()) {
- if (!d->statusMonitor.isMonitoring())
- d->statusMonitor.start();
- return d->networkAccessible;
- }
+ \since 4.6
- if (d->customNetworkConfiguration && d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
- return UnknownAccessibility;
-
- if (d->networkSessionRequired) {
- QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
- if (networkSession) {
- // d->online holds online/offline state of this network session.
- if (d->online)
- return d->networkAccessible;
- else
- return NotAccessible;
- } else {
- if (d->defaultAccessControl) {
- if (d->online)
- return d->networkAccessible;
- else
- return NotAccessible;
- }
- return (d->networkAccessible);
- }
- } else {
- if (d->online)
- return d->networkAccessible;
- else
- return NotAccessible;
- }
-}
+ Sends a request to delete the resource identified by the URL of \a request.
-/*!
- \internal
+ \note This feature is currently available for HTTP only, performing an
+ HTTP DELETE request.
- Returns the network session currently in use.
- This can be changed at any time, ownership remains with the QNetworkAccessManager
+ \sa get(), post(), put(), sendCustomRequest()
*/
-const QWeakPointer<const QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession(const QNetworkAccessManager *q)
-{
- return q->d_func()->networkSessionWeakRef;
-}
-
-QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession() const
+QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
{
- if (networkSessionStrongRef)
- return networkSessionStrongRef;
- return networkSessionWeakRef.toStrongRef();
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
}
-#endif // QT_NO_BEARERMANAGEMENT
-
#ifndef QT_NO_SSL
/*!
\since 5.2
@@ -1188,9 +997,9 @@ QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession(
\a sslConfiguration. This function is useful to complete the TCP and SSL handshake
to a host before the HTTPS request is made, resulting in a lower network latency.
- \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols()
- on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in
- the list of allowed protocols. When using SPDY, one single connection per host is
+ \note Preconnecting a HTTP/2 connection can be done by calling setAllowedNextProtocols()
+ on \a sslConfiguration with QSslConfiguration::ALPNProtocolHTTP2 contained in
+ the list of allowed protocols. When using HTTP/2, one single connection per host is
enough, i.e. calling this method multiple times per host will not result in faster
network transactions.
@@ -1214,9 +1023,9 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
validation. This function is useful to complete the TCP and SSL handshake
to a host before the HTTPS request is made, resulting in a lower network latency.
- \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols()
- on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in
- the list of allowed protocols. When using SPDY, one single connection per host is
+ \note Preconnecting a HTTP/2 connection can be done by calling setAllowedNextProtocols()
+ on \a sslConfiguration with QSslConfiguration::ALPNProtocolHTTP2 contained in
+ the list of allowed protocols. When using HTTP/2, one single connection per host is
enough, i.e. calling this method multiple times per host will not result in faster
network transactions.
@@ -1232,17 +1041,15 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
QUrl url;
url.setHost(hostName);
url.setPort(port);
- url.setScheme(QLatin1String("preconnect-https"));
+ url.setScheme("preconnect-https"_L1);
QNetworkRequest request(url);
if (sslConfiguration != QSslConfiguration::defaultConfiguration())
request.setSslConfiguration(sslConfiguration);
- // There is no way to enable SPDY/HTTP2 via a request, so we need to check
- // the ssl configuration whether SPDY/HTTP2 is allowed here.
- if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2))
- request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
- else if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::NextProtocolSpdy3_0))
- request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true);
+ // There is no way to enable HTTP2 via a request after having established the connection,
+ // so we need to check the ssl configuration whether HTTP2 is allowed here.
+ if (!sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2))
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
request.setPeerVerifyName(peerName);
get(request);
@@ -1265,7 +1072,7 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
QUrl url;
url.setHost(hostName);
url.setPort(port);
- url.setScheme(QLatin1String("preconnect-http"));
+ url.setScheme("preconnect-http"_L1);
QNetworkRequest request(url);
get(request);
}
@@ -1279,16 +1086,13 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
Use this function to enable or disable HTTP redirects on the manager's level.
\note When creating a request QNetworkRequest::RedirectAttributePolicy has
- the highest priority, next by priority is QNetworkRequest::FollowRedirectsAttribute.
- Finally, the manager's policy has the lowest priority.
+ the highest priority, next by priority the manager's policy.
- For backwards compatibility the default value is QNetworkRequest::ManualRedirectPolicy.
- This may change in the future and some type of auto-redirect policy will become
- the default; clients relying on manual redirect handling are encouraged to set
+ The default value is QNetworkRequest::NoLessSafeRedirectPolicy.
+ Clients relying on manual redirect handling are encouraged to set
this policy explicitly in their code.
- \sa redirectPolicy(), QNetworkRequest::RedirectPolicy,
- QNetworkRequest::FollowRedirectsAttribute
+ \sa redirectPolicy(), QNetworkRequest::RedirectPolicy
*/
void QNetworkAccessManager::setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
{
@@ -1354,7 +1158,7 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
return reply;
}
-#if QT_CONFIG(http)
+#if QT_CONFIG(http) || defined(Q_OS_WASM)
/*!
\since 5.8
@@ -1398,15 +1202,14 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
Q_D(QNetworkAccessManager);
QNetworkRequest req(originalReq);
- if (redirectPolicy() != QNetworkRequest::ManualRedirectPolicy
- && req.attribute(QNetworkRequest::RedirectPolicyAttribute).isNull()
- && req.attribute(QNetworkRequest::FollowRedirectsAttribute).isNull()) {
+ if (redirectPolicy() != QNetworkRequest::NoLessSafeRedirectPolicy
+ && req.attribute(QNetworkRequest::RedirectPolicyAttribute).isNull()) {
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, redirectPolicy());
}
-#if QT_CONFIG(http)
- if (!req.transferTimeout())
- req.setTransferTimeout(transferTimeout());
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+ if (req.transferTimeoutAsDuration() == 0ms)
+ req.setTransferTimeout(transferTimeoutAsDuration());
#endif
if (autoDeleteReplies()
@@ -1417,16 +1220,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
bool isLocalFile = req.url().isLocalFile();
QString scheme = req.url().scheme();
-#ifdef Q_OS_WASM
- // Support http, https, and relateive urls
- if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) {
- QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
- QNetworkReplyWasmImplPrivate *priv = reply->d_func();
- priv->manager = this;
- priv->setup(op, req, outgoingData);
- return reply;
+ // Remap local+http to unix+http to make further processing easier
+ if (scheme == "local+http"_L1) {
+ scheme = u"unix+http"_s;
+ QUrl url = req.url();
+ url.setScheme(scheme);
+ req.setUrl(url);
}
-#endif
// fast path for GET on file:// URLs
// The QNetworkAccessFileBackend will right now only be used for PUT
@@ -1434,13 +1234,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|| op == QNetworkAccessManager::HeadOperation) {
if (isLocalFile
#ifdef Q_OS_ANDROID
- || scheme == QLatin1String("assets")
+ || scheme == "assets"_L1
#endif
- || scheme == QLatin1String("qrc")) {
+ || scheme == "qrc"_L1) {
return new QNetworkReplyFileImpl(this, req, op);
}
- if (scheme == QLatin1String("data"))
+ if (scheme == "data"_L1)
return new QNetworkReplyDataImpl(this, req, op);
// A request with QNetworkRequest::AlwaysCache does not need any bearer management
@@ -1454,93 +1254,64 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QNetworkReplyImplPrivate *priv = reply->d_func();
priv->manager = this;
priv->backend = new QNetworkAccessCacheBackend();
- priv->backend->manager = this->d_func();
+ priv->backend->setManagerPrivate(this->d_func());
priv->backend->setParent(reply);
- priv->backend->reply = priv;
+ priv->backend->setReplyPrivate(priv);
priv->setup(op, req, outgoingData);
return reply;
}
}
-
- if (d->statusMonitor.isEnabled()) {
- // See the code in ctor - QNetworkStatusMonitor allows us to
- // immediately set 'networkAccessible' even before we start
- // the monitor.
-#ifdef QT_NO_BEARERMANAGEMENT
- if (d->networkAccessible
-#else
- if (d->networkAccessible == NotAccessible
-#endif // QT_NO_BEARERMANAGEMENT
- && !isLocalFile) {
- QHostAddress dest;
- QString host = req.url().host().toLower();
- if (!(dest.setAddress(host) && dest.isLoopback())
- && host != QLatin1String("localhost")
- && host != QHostInfo::localHostName().toLower()) {
- return new QDisabledNetworkReply(this, req, op);
- }
- }
-
- if (!d->statusMonitor.isMonitoring() && !d->statusMonitor.start())
- qWarning(lcNetMon, "failed to start network status monitoring");
- } else {
-#ifndef QT_NO_BEARERMANAGEMENT
- // Return a disabled network reply if network access is disabled.
- // Except if the scheme is empty or file:// or if the host resolves to a loopback address.
- if (d->networkAccessible == NotAccessible && !isLocalFile) {
- QHostAddress dest;
- QString host = req.url().host().toLower();
- if (!(dest.setAddress(host) && dest.isLoopback()) && host != QLatin1String("localhost")
- && host != QHostInfo::localHostName().toLower()) {
- return new QDisabledNetworkReply(this, req, op);
- }
- }
-
- if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) {
- if (!d->networkConfiguration.identifier().isEmpty()) {
- if ((d->networkConfiguration.state() & QNetworkConfiguration::Defined)
- && d->networkConfiguration != d->networkConfigurationManager.defaultConfiguration())
- d->createSession(d->networkConfigurationManager.defaultConfiguration());
- else
- d->createSession(d->networkConfiguration);
-
- } else {
- if (d->networkSessionRequired)
- d->createSession(d->networkConfigurationManager.defaultConfiguration());
- else
- d->initializeSession = false;
- }
- }
-#endif
- }
-
QNetworkRequest request = req;
- if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
+ auto h = request.headers();
+#ifndef Q_OS_WASM // Content-length header is not allowed to be set by user in wasm
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentLength) &&
outgoingData && !outgoingData->isSequential()) {
// request has no Content-Length
// but the data that is outgoing is random-access
- request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
+ h.append(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(outgoingData->size()));
}
-
+#endif
if (static_cast<QNetworkRequest::LoadControl>
(request.attribute(QNetworkRequest::CookieLoadControlAttribute,
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
if (d->cookieJar) {
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::Cookie,
+ QNetworkHeadersPrivate::fromCookieList(cookies));
}
}
+ request.setHeaders(std::move(h));
+#ifdef Q_OS_WASM
+ Q_UNUSED(isLocalFile);
+ // Support http, https, and relative urls
+ if (scheme == "http"_L1 || scheme == "https"_L1 || scheme.isEmpty()) {
+ QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
+ QNetworkReplyWasmImplPrivate *priv = reply->d_func();
+ priv->manager = this;
+ priv->setup(op, request, outgoingData);
+ return reply;
+ }
+#endif
#if QT_CONFIG(http)
- // Since Qt 5 we use the new QNetworkReplyHttpImpl
- if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http")
+ constexpr char16_t httpSchemes[][17] = {
+ u"http",
+ u"preconnect-http",
#ifndef QT_NO_SSL
- || scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https")
+ u"https",
+ u"preconnect-https",
#endif
- ) {
+ u"unix+http",
+ };
+ // Since Qt 5 we use the new QNetworkReplyHttpImpl
+ if (std::find(std::begin(httpSchemes), std::end(httpSchemes), scheme) != std::end(httpSchemes)) {
+
#ifndef QT_NO_SSL
- if (isStrictTransportSecurityEnabled() && d->stsCache.isKnownHost(request.url())) {
+ const bool isLocalSocket = scheme.startsWith("unix"_L1);
+ if (!isLocalSocket && isStrictTransportSecurityEnabled()
+ && d->stsCache.isKnownHost(request.url())) {
QUrl stsUrl(request.url());
// RFC6797, 8.3:
// The UA MUST replace the URI scheme with "https" [RFC2818],
@@ -1553,31 +1324,17 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
// MUST NOT add one.
if (stsUrl.port() == 80)
stsUrl.setPort(443);
- stsUrl.setScheme(QLatin1String("https"));
+ stsUrl.setScheme("https"_L1);
request.setUrl(stsUrl);
}
#endif
QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
-#ifndef QT_NO_BEARERMANAGEMENT
- if (!d->statusMonitor.isEnabled()) {
- connect(this, SIGNAL(networkSessionConnected()),
- reply, SLOT(_q_networkSessionConnected()));
- }
-#endif
return reply;
}
#endif // QT_CONFIG(http)
// first step: create the reply
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
-#ifndef QT_NO_BEARERMANAGEMENT
- // NETMONTODO: network reply impl must be augmented to use the same monitoring
- // capabilities as http network reply impl does. Once it does: uncomment the condition below
- if (!isLocalFile /*&& !d->statusMonitor.isEnabled()*/) {
- connect(this, SIGNAL(networkSessionConnected()),
- reply, SLOT(_q_networkSessionConnected()));
- }
-#endif
QNetworkReplyImplPrivate *priv = reply->d_func();
priv->manager = this;
@@ -1590,7 +1347,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (priv->backend) {
priv->backend->setParent(reply);
- priv->backend->reply = priv;
+ priv->backend->setReplyPrivate(priv);
}
#ifndef QT_NO_SSL
@@ -1608,7 +1365,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
Lists all the URL schemes supported by the access manager.
- \sa supportedSchemesImplementation()
+ Reimplement this method to provide your own supported schemes
+ in a QNetworkAccessManager subclass. It is for instance necessary
+ when your subclass provides support for new protocols.
*/
QStringList QNetworkAccessManager::supportedSchemes() const
{
@@ -1622,19 +1381,16 @@ QStringList QNetworkAccessManager::supportedSchemes() const
/*!
\since 5.2
+ \deprecated
Lists all the URL schemes supported by the access manager.
You should not call this function directly; use
QNetworkAccessManager::supportedSchemes() instead.
- Reimplement this slot to provide your own supported schemes
- in a QNetworkAccessManager subclass. It is for instance necessary
- when your subclass provides support for new protocols.
-
Because of binary compatibility constraints, the supportedSchemes()
- method (introduced in Qt 5.2) is not virtual. Instead, supportedSchemes()
- will dynamically detect and call this slot.
+ method (introduced in Qt 5.2) was not virtual in Qt 5, but now it
+ is. Override the supportedSchemes method rather than this one.
\sa supportedSchemes()
*/
@@ -1646,6 +1402,8 @@ QStringList QNetworkAccessManager::supportedSchemesImplementation() const
// Those ones don't exist in backends
#if QT_CONFIG(http)
schemes << QStringLiteral("http");
+ schemes << QStringLiteral("unix+http");
+ schemes << QStringLiteral("local+http");
#ifndef QT_NO_SSL
if (QSslSocket::supportsSsl())
schemes << QStringLiteral("https");
@@ -1719,68 +1477,77 @@ void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete)
}
/*!
+ \fn int QNetworkAccessManager::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout() hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
+*/
+
+/*!
+ \fn void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \since 5.15
+
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
*/
-int QNetworkAccessManager::transferTimeout() const
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
+ */
+std::chrono::milliseconds QNetworkAccessManager::transferTimeoutAsDuration() const
{
return d_func()->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::TransferTimeoutPreset. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero. The request-specific non-zero timeouts set for
the requests that are executed override this value. This means
that if QNetworkAccessManager has an enabled timeout, it needs
to be disabled to execute a request without a timeout.
- \sa transferTimeout()
-*/
-void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \sa transferTimeoutAsDuration()
+ */
+void QNetworkAccessManager::setTransferTimeout(std::chrono::milliseconds duration)
{
- d_func()->transferTimeout = timeout;
+ d_func()->transferTimeout = duration;
}
-void QNetworkAccessManagerPrivate::_q_replyFinished()
+void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply)
{
Q_Q(QNetworkAccessManager);
- QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
- if (reply) {
- emit q->finished(reply);
- if (reply->request().attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, false).toBool())
- QMetaObject::invokeMethod(reply, [reply] { reply->deleteLater(); }, Qt::QueuedConnection);
- }
-
-#ifndef QT_NO_BEARERMANAGEMENT
- // If there are no active requests, release our reference to the network session.
- // It will not be destroyed immediately, but rather when the connection cache is flushed
- // after 2 minutes.
- activeReplyCount--;
- if (networkSessionStrongRef && activeReplyCount == 0)
- networkSessionStrongRef.clear();
-#endif
+ emit q->finished(reply);
+ if (reply->request().attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, false).toBool())
+ QMetaObject::invokeMethod(reply, [reply] { reply->deleteLater(); }, Qt::QueuedConnection);
}
-void QNetworkAccessManagerPrivate::_q_replyEncrypted()
+void QNetworkAccessManagerPrivate::_q_replyEncrypted(QNetworkReply *reply)
{
#ifndef QT_NO_SSL
Q_Q(QNetworkAccessManager);
- QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
- if (reply)
- emit q->encrypted(reply);
+ emit q->encrypted(reply);
+#else
+ Q_UNUSED(reply);
#endif
}
@@ -1796,33 +1563,30 @@ void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &err
#endif
}
+#ifndef QT_NO_SSL
void QNetworkAccessManagerPrivate::_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
{
-#ifndef QT_NO_SSL
Q_Q(QNetworkAccessManager);
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
if (reply)
emit q->preSharedKeyAuthenticationRequired(reply, authenticator);
-#else
- Q_UNUSED(authenticator);
-#endif
}
+#endif
QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
{
Q_Q(QNetworkAccessManager);
QNetworkReplyPrivate::setManager(reply, q);
- q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));
+ q->connect(reply, &QNetworkReply::finished, reply,
+ [this, reply]() { _q_replyFinished(reply); });
#ifndef QT_NO_SSL
/* In case we're compiled without SSL support, we don't have this signal and we need to
* avoid getting a connection error. */
- q->connect(reply, SIGNAL(encrypted()), SLOT(_q_replyEncrypted()));
+ q->connect(reply, &QNetworkReply::encrypted, reply,
+ [this, reply]() { _q_replyEncrypted(reply); });
q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
q->connect(reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), SLOT(_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- activeReplyCount++;
-#endif
return reply;
}
@@ -1851,9 +1615,10 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authen
// also called when last URL is empty, e.g. on first call
if (allowAuthenticationReuse && (urlForLastAuthentication->isEmpty()
|| url != *urlForLastAuthentication)) {
- // if credentials are included in the url, then use them
- if (!url.userName().isEmpty()
- && !url.password().isEmpty()) {
+ // if credentials are included in the url, then use them, unless they were already used
+ if (!url.userName().isEmpty() && !url.password().isEmpty()
+ && (url.userName() != authenticator->user()
+ || url.password() != authenticator->password())) {
authenticator->setUser(url.userName(QUrl::FullyDecoded));
authenticator->setPassword(url.password(QUrl::FullyDecoded));
*urlForLastAuthentication = url;
@@ -1862,7 +1627,8 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authen
}
QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
- if (!cred.isNull()) {
+ if (!cred.isNull()
+ && (cred.user != authenticator->user() || cred.password != authenticator->password())) {
authenticator->setUser(cred.user);
authenticator->setPassword(cred.password);
*urlForLastAuthentication = url;
@@ -1899,7 +1665,7 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(const QUrl &url,
}
}
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
//now we try to get the username and password from keychain
//if not successful signal will be emitted
QString username;
@@ -1990,234 +1756,19 @@ void QNetworkAccessManagerPrivate::destroyThread()
}
}
-#ifndef QT_NO_BEARERMANAGEMENT
-void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config)
-{
- Q_Q(QNetworkAccessManager);
-
- initializeSession = false;
-
- //resurrect weak ref if possible
- networkSessionStrongRef = networkSessionWeakRef.toStrongRef();
-
- QSharedPointer<QNetworkSession> newSession;
- if (config.isValid())
- newSession = QSharedNetworkSessionManager::getSession(config);
-
- QNetworkSession::State oldState = QNetworkSession::Invalid;
- if (networkSessionStrongRef) {
- //do nothing if new and old session are the same
- if (networkSessionStrongRef == newSession)
- return;
- //disconnect from old session
- QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
- QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
- QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)),
- q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
- QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)),
- q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError)));
- oldState = networkSessionStrongRef->state();
- }
-
- //switch to new session (null if config was invalid)
- networkSessionStrongRef = newSession;
- networkSessionWeakRef = networkSessionStrongRef.toWeakRef();
-
- if (!networkSessionStrongRef) {
-
- if (networkAccessible == QNetworkAccessManager::NotAccessible || !online)
- emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
- else
- emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility);
-
- return;
- }
-
- //connect to new session
- QObject::connect(networkSessionStrongRef.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection);
- //QueuedConnection is used to avoid deleting the networkSession inside its closed signal
- QObject::connect(networkSessionStrongRef.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection);
- QObject::connect(networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)),
- q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection);
- QObject::connect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)),
- q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError)));
-
- const QNetworkSession::State newState = networkSessionStrongRef->state();
- if (newState != oldState) {
- QMetaObject::invokeMethod(q, "_q_networkSessionStateChanged", Qt::QueuedConnection,
- Q_ARG(QNetworkSession::State, newState));
- }
-}
-
-void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
-{
- Q_Q(QNetworkAccessManager);
- QSharedPointer<QNetworkSession> networkSession(getNetworkSession());
- if (networkSession) {
- networkConfiguration = networkSession->configuration();
-
- //disconnect from old session
- QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
- QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
- QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
- q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
- QObject::disconnect(networkSession.data(), SIGNAL(error(QNetworkSession::SessionError)),
- q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError)));
-
- networkSessionStrongRef.clear();
- networkSessionWeakRef.clear();
- }
-}
-
-void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state)
-{
- Q_Q(QNetworkAccessManager);
- bool reallyOnline = false;
- //Do not emit the networkSessionConnected signal here, except for roaming -> connected
- //transition, otherwise it is emitted twice in a row when opening a connection.
- if (state == QNetworkSession::Connected && lastSessionState != QNetworkSession::Roaming)
- emit q->networkSessionConnected();
- lastSessionState = state;
-
- if (online && (state == QNetworkSession::Disconnected
- || state == QNetworkSession::NotAvailable)) {
- const auto cfgs = networkConfigurationManager.allConfigurations();
- for (const QNetworkConfiguration &cfg : cfgs) {
- if (cfg.state().testFlag(QNetworkConfiguration::Active)) {
- reallyOnline = true;
- }
- }
- } else if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
- reallyOnline = true;
- }
- online = reallyOnline;
-
- if (!reallyOnline) {
- if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
- if (networkAccessible != QNetworkAccessManager::NotAccessible) {
- networkAccessible = QNetworkAccessManager::NotAccessible;
- emit q->networkAccessibleChanged(networkAccessible);
- }
- }
- } else {
- if (defaultAccessControl)
- if (networkAccessible != QNetworkAccessManager::Accessible) {
- networkAccessible = QNetworkAccessManager::Accessible;
- emit q->networkAccessibleChanged(networkAccessible);
- }
- }
- if (online && (state != QNetworkSession::Connected && state != QNetworkSession::Roaming)) {
- _q_networkSessionClosed();
- createSession(q->configuration());
- }
-}
-
-void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline)
-{
- Q_Q(QNetworkAccessManager);
-
- if (statusMonitor.isEnabled()) {
- networkAccessible = isOnline ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible;
- return;
- }
-
-
- // if the user set a config, we only care whether this one is active.
- // Otherwise, this QNAM is online if there is an online config.
- if (customNetworkConfiguration) {
- online = (networkConfiguration.state() & QNetworkConfiguration::Active);
- } else {
- if (online != isOnline) {
- online = isOnline;
- _q_networkSessionClosed();
- createSession(q->configuration());
- }
- }
- if (online) {
- if (defaultAccessControl) {
- if (networkAccessible != QNetworkAccessManager::Accessible) {
- networkAccessible = QNetworkAccessManager::Accessible;
- emit q->networkAccessibleChanged(networkAccessible);
- }
- }
- } else {
- if (networkAccessible != QNetworkAccessManager::NotAccessible) {
- networkAccessible = QNetworkAccessManager::NotAccessible;
- emit q->networkAccessibleChanged(networkAccessible);
- }
- }
-}
-
-void QNetworkAccessManagerPrivate::_q_configurationChanged(const QNetworkConfiguration &configuration)
-{
- if (statusMonitor.isEnabled())
- return;
-
- const QString id = configuration.identifier();
- if (configuration.state().testFlag(QNetworkConfiguration::Active)) {
- if (!onlineConfigurations.contains(id)) {
- QSharedPointer<QNetworkSession> session(getNetworkSession());
- if (session) {
- if (online && session->configuration().identifier()
- != networkConfigurationManager.defaultConfiguration().identifier()) {
-
- onlineConfigurations.insert(id);
- // CHECK: If it's having Active flag - why would it be disconnected ???
- //this one disconnected but another one is online,
- // close and create new session
- _q_networkSessionClosed();
- createSession(networkConfigurationManager.defaultConfiguration());
- }
- }
- }
-
- } else if (onlineConfigurations.contains(id)) {
- //this one is disconnecting
- // CHECK: If it disconnected while we create a session over a down configuration ???
- onlineConfigurations.remove(id);
- if (!onlineConfigurations.isEmpty()) {
- _q_networkSessionClosed();
- createSession(configuration);
- }
- }
-}
-
-
-void QNetworkAccessManagerPrivate::_q_networkSessionFailed(QNetworkSession::SessionError)
-{
- if (statusMonitor.isEnabled())
- return;
-
- const auto cfgs = networkConfigurationManager.allConfigurations();
- for (const QNetworkConfiguration &cfg : cfgs) {
- if (cfg.state().testFlag(QNetworkConfiguration::Active)) {
- online = true;
- _q_networkSessionClosed();
- createSession(networkConfigurationManager.defaultConfiguration());
- return;
- }
- }
-}
-
-#else
-
-void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline)
-{
- networkAccessible = isOnline;
-}
-#endif // QT_NO_BEARERMANAGEMENT
+#if QT_CONFIG(http) || defined(Q_OS_WASM)
-#if QT_CONFIG(http)
QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
{
// copy the request, we probably need to add some headers
QNetworkRequest newRequest(request);
+ auto h = newRequest.headers();
// add Content-Type header if not there already
- if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
QByteArray contentType;
- contentType.reserve(34 + multiPart->d_func()->boundary.count());
+ contentType.reserve(34 + multiPart->d_func()->boundary.size());
contentType += "multipart/";
switch (multiPart->d_func()->contentType) {
case QHttpMultiPart::RelatedType:
@@ -2235,14 +1786,15 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
}
// putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
contentType += "; boundary=\"" + multiPart->d_func()->boundary + '"';
- newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
+ h.append(QHttpHeaders::WellKnownHeader::ContentType, contentType);
}
// add MIME-Version header if not there already (we must include the header
// if the message conforms to RFC 2045, see section 4 of that RFC)
- QByteArray mimeHeader("MIME-Version");
- if (!request.hasRawHeader(mimeHeader))
- newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
+ if (!h.contains(QHttpHeaders::WellKnownHeader::MIMEVersion))
+ h.append(QHttpHeaders::WellKnownHeader::MIMEVersion, "1.0"_ba);
+
+ newRequest.setHeaders(std::move(h));
QIODevice *device = multiPart->d_func()->device;
if (!device->isReadable()) {
@@ -2258,6 +1810,25 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
}
#endif // QT_CONFIG(http)
+/*!
+ \internal
+ Go through the instances so the factories will be created and
+ register themselves to QNetworkAccessBackendFactoryData
+*/
+void QNetworkAccessManagerPrivate::ensureBackendPluginsLoaded()
+{
+ Q_CONSTINIT static QBasicMutex mutex;
+ std::unique_lock locker(mutex);
+ if (!qnabfLoader())
+ return;
+#if QT_CONFIG(library)
+ qnabfLoader->update();
+#endif
+ int index = 0;
+ while (qnabfLoader->instance(index))
+ ++index;
+}
+
QT_END_NAMESPACE
#include "moc_qnetworkaccessmanager.cpp"
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index aa4765a043..0d069b2a9b 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSMANAGER_H
#define QNETWORKACCESSMANAGER_H
@@ -43,12 +7,13 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtCore/QString>
-#include <QtCore/QVector>
+#include <QtCore/QList>
#include <QtCore/QObject>
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslPreSharedKeyAuthenticator>
#endif
+Q_MOC_INCLUDE(<QtNetwork/QSslError>)
QT_BEGIN_NAMESPACE
@@ -56,7 +21,6 @@ class QIODevice;
class QAbstractNetworkCache;
class QAuthenticator;
class QByteArray;
-template<typename T> class QList;
class QNetworkCookie;
class QNetworkCookieJar;
class QNetworkReply;
@@ -64,9 +28,6 @@ class QNetworkProxy;
class QNetworkProxyFactory;
class QSslError;
class QHstsPolicy;
-#ifndef QT_NO_BEARERMANAGEMENT
-class QNetworkConfiguration;
-#endif
class QHttpMultiPart;
class QNetworkReplyImplPrivate;
@@ -75,9 +36,6 @@ class Q_NETWORK_EXPORT QNetworkAccessManager: public QObject
{
Q_OBJECT
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_PROPERTY(NetworkAccessibility networkAccessible READ networkAccessible WRITE setNetworkAccessible NOTIFY networkAccessibleChanged)
-#endif
public:
enum Operation {
@@ -91,20 +49,10 @@ public:
UnknownOperation = 0
};
-#ifndef QT_NO_BEARERMANAGEMENT
- enum NetworkAccessibility {
- UnknownAccessibility = -1,
- NotAccessible = 0,
- Accessible = 1
- };
- Q_ENUM(NetworkAccessibility)
-#endif
-
explicit QNetworkAccessManager(QObject *parent = nullptr);
~QNetworkAccessManager();
- // ### Qt 6: turn into virtual
- QStringList supportedSchemes() const;
+ virtual QStringList supportedSchemes() const;
void clearAccessCache();
@@ -127,34 +75,29 @@ public:
bool isStrictTransportSecurityEnabled() const;
void enableStrictTransportSecurityStore(bool enabled, const QString &storeDir = QString());
bool isStrictTransportSecurityStoreEnabled() const;
- void addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts);
- QVector<QHstsPolicy> strictTransportSecurityHosts() const;
+ void addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts);
+ QList<QHstsPolicy> strictTransportSecurityHosts() const;
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
+ QNetworkReply *get(const QNetworkRequest &request, QIODevice *data);
+ QNetworkReply *get(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *post(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *put(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = nullptr);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data);
-#if QT_CONFIG(http)
+#if QT_CONFIG(http) || defined(Q_OS_WASM)
QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart);
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- void setConfiguration(const QNetworkConfiguration &config);
- QNetworkConfiguration configuration() const;
- QNetworkConfiguration activeConfiguration() const;
-
- void setNetworkAccessible(NetworkAccessibility accessible);
- NetworkAccessibility networkAccessible() const;
-#endif
-
#ifndef QT_NO_SSL
void connectToHostEncrypted(const QString &hostName, quint16 port = 443,
const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration());
@@ -170,8 +113,14 @@ public:
bool autoDeleteReplies() const;
void setAutoDeleteReplies(bool autoDelete);
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = QNetworkRequest::TransferTimeoutPreset);
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration =
+ QNetworkRequest::DefaultTransferTimeout);
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
@@ -185,12 +134,6 @@ Q_SIGNALS:
void preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator);
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- void networkSessionConnected();
-
- void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
-#endif
-
protected:
virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
QIODevice *outgoingData = nullptr);
@@ -208,19 +151,24 @@ private:
friend class QNetworkReplyWasmImpl;
#endif
Q_DECLARE_PRIVATE(QNetworkAccessManager)
- Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
- Q_PRIVATE_SLOT(d_func(), void _q_replyEncrypted())
Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>))
+#ifndef QT_NO_SSL
Q_PRIVATE_SLOT(d_func(), void _q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed())
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))
- Q_PRIVATE_SLOT(d_func(), void _q_configurationChanged(const QNetworkConfiguration &))
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed(QNetworkSession::SessionError))
#endif
- Q_PRIVATE_SLOT(d_func(), void _q_onlineStateChanged(bool))
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+int QNetworkAccessManager::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkAccessManager::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index d558f61eed..491a5acaa4 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSMANAGER_P_H
#define QNETWORKACCESSMANAGER_P_H
@@ -60,11 +24,7 @@
#include "qhsts_p.h"
#include "private/qobject_p.h"
#include "QtNetwork/qnetworkproxy.h"
-#include "QtNetwork/qnetworksession.h"
#include "qnetworkaccessauthenticationmanager_p.h"
-#ifndef QT_NO_BEARERMANAGEMENT
-#include "QtNetwork/qnetworkconfigmanager.h"
-#endif
#if QT_CONFIG(settings)
#include "qhstsstore_p.h"
@@ -87,41 +47,19 @@ public:
#ifndef QT_NO_NETWORKPROXY
proxyFactory(nullptr),
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- lastSessionState(QNetworkSession::Invalid),
- networkConfiguration(networkConfigurationManager.defaultConfiguration()),
- customNetworkConfiguration(false),
- networkSessionRequired(networkConfigurationManager.capabilities()
- & QNetworkConfigurationManager::NetworkSessionRequired),
- activeReplyCount(0),
- online(false),
- initializeSession(true),
-#endif
cookieJarCreated(false),
defaultAccessControl(true),
- redirectPolicy(QNetworkRequest::ManualRedirectPolicy),
- authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
+ redirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy),
+ authenticationManager(std::make_shared<QNetworkAccessAuthenticationManager>())
{
-#ifndef QT_NO_BEARERMANAGEMENT
- // we would need all active configurations to check for
- // d->networkConfigurationManager.isOnline(), which is asynchronous
- // and potentially expensive. We can just check the configuration here
- online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active));
- if (online)
- networkAccessible = QNetworkAccessManager::Accessible;
- else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
- networkAccessible = QNetworkAccessManager::UnknownAccessibility;
- else
- networkAccessible = QNetworkAccessManager::NotAccessible;
-#endif
}
~QNetworkAccessManagerPrivate();
QThread * createThread();
void destroyThread();
- void _q_replyFinished();
- void _q_replyEncrypted();
+ void _q_replyFinished(QNetworkReply *reply);
+ void _q_replyEncrypted(QNetworkReply *reply);
void _q_replySslErrors(const QList<QSslError> &errors);
void _q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
QNetworkReply *postProcess(QNetworkReply *reply);
@@ -152,27 +90,12 @@ public:
QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request);
QStringList backendSupportedSchemes() const;
- void _q_onlineStateChanged(bool isOnline);
-#ifndef QT_NO_BEARERMANAGEMENT
- void createSession(const QNetworkConfiguration &config);
- QSharedPointer<QNetworkSession> getNetworkSession() const;
-
- void _q_networkSessionClosed();
- void _q_networkSessionNewConfigurationActivated();
- void _q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &config,
- bool isSeamless);
- void _q_networkSessionStateChanged(QNetworkSession::State state);
-
- void _q_configurationChanged(const QNetworkConfiguration &configuration);
- void _q_networkSessionFailed(QNetworkSession::SessionError error);
-
- QSet<QString> onlineConfigurations;
-#endif
-
-#if QT_CONFIG(http)
+#if QT_CONFIG(http) || defined(Q_OS_WASM)
QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
#endif
+ void ensureBackendPluginsLoaded();
+
// this is the cache for storing downloaded files
QAbstractNetworkCache *networkCache;
@@ -186,36 +109,16 @@ public:
QNetworkProxyFactory *proxyFactory;
#endif
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSessionStrongRef;
- QWeakPointer<QNetworkSession> networkSessionWeakRef;
- QNetworkSession::State lastSessionState;
- QNetworkConfigurationManager networkConfigurationManager;
- QNetworkConfiguration networkConfiguration;
- // we need to track whether the user set a config or not,
- // because the default config might change
- bool customNetworkConfiguration;
- bool networkSessionRequired;
- QNetworkAccessManager::NetworkAccessibility networkAccessible;
- int activeReplyCount;
- bool online;
- bool initializeSession;
-#else
- bool networkAccessible = true;
-#endif
-
bool cookieJarCreated;
bool defaultAccessControl;
- QNetworkRequest::RedirectPolicy redirectPolicy;
+ QNetworkRequest::RedirectPolicy redirectPolicy = QNetworkRequest::NoLessSafeRedirectPolicy;
// The cache with authorization data:
- QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+ std::shared_ptr<QNetworkAccessAuthenticationManager> authenticationManager;
// this cache can be used by individual backends to cache e.g. their TCP connections to a server
// and use the connections for multiple requests.
QNetworkAccessCache objectCache;
- static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
- { return &backend->manager->objectCache; }
Q_AUTOTEST_EXPORT static void clearAuthenticationCache(QNetworkAccessManager *manager);
Q_AUTOTEST_EXPORT static void clearConnectionCache(QNetworkAccessManager *manager);
@@ -225,15 +128,11 @@ public:
QScopedPointer<QHstsStore> stsStore;
#endif // QT_CONFIG(settings)
bool stsEnabled = false;
- mutable QNetworkStatusMonitor statusMonitor;
bool autoDeleteReplies = false;
- int transferTimeout = 0;
+ std::chrono::milliseconds transferTimeout{0};
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_AUTOTEST_EXPORT static const QWeakPointer<const QNetworkSession> getNetworkSession(const QNetworkAccessManager *manager);
-#endif
Q_DECLARE_PUBLIC(QNetworkAccessManager)
};
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 47f6112b22..8ea5fdbe57 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkcookie.h"
#include "qnetworkcookie_p.h"
@@ -43,18 +7,24 @@
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "QtCore/qbytearray.h"
+#include "QtCore/qdatetime.h"
#include "QtCore/qdebug.h"
#include "QtCore/qlist.h"
#include "QtCore/qlocale.h"
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include "QtCore/qstring.h"
#include "QtCore/qstringlist.h"
+#include "QtCore/qtimezone.h"
#include "QtCore/qurl.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN(QNetworkCookie)
+
/*!
\class QNetworkCookie
\since 4.4
@@ -180,7 +150,8 @@ bool QNetworkCookie::operator==(const QNetworkCookie &other) const
d->domain == other.d->domain &&
d->path == other.d->path &&
d->secure == other.d->secure &&
- d->comment == other.d->comment;
+ d->comment == other.d->comment &&
+ d->sameSite == other.d->sameSite;
}
/*!
@@ -222,6 +193,29 @@ void QNetworkCookie::setSecure(bool enable)
}
/*!
+ Returns the "SameSite" option if specified in the cookie
+ string, \c SameSite::Default if not present.
+
+ \since 6.1
+ \sa setSameSitePolicy()
+*/
+QNetworkCookie::SameSite QNetworkCookie::sameSitePolicy() const
+{
+ return d->sameSite;
+}
+
+/*!
+ Sets the "SameSite" option of this cookie to \a sameSite.
+
+ \since 6.1
+ \sa sameSitePolicy()
+*/
+void QNetworkCookie::setSameSitePolicy(QNetworkCookie::SameSite sameSite)
+{
+ d->sameSite = sameSite;
+}
+
+/*!
\since 4.5
Returns \c true if the "HttpOnly" flag is enabled for this cookie.
@@ -382,13 +376,13 @@ void QNetworkCookie::setValue(const QByteArray &value)
}
// ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
-static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
+static QPair<QByteArray, QByteArray> nextField(QByteArrayView text, int &position, bool isNameValue)
{
// format is one of:
// (1) token
// (2) token = token
// (3) token = quoted-string
- const int length = text.length();
+ const int length = text.size();
position = nextNonWhitespace(text, position);
int semiColonPosition = text.indexOf(';', position);
@@ -402,11 +396,11 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
equalsPosition = semiColonPosition; //no '=' means there is an attribute-name but no attribute-value
}
- QByteArray first = text.mid(position, equalsPosition - position).trimmed();
+ QByteArray first = text.mid(position, equalsPosition - position).trimmed().toByteArray();
QByteArray second;
int secondLength = semiColonPosition - equalsPosition - 1;
if (secondLength > 0)
- second = text.mid(equalsPosition + 1, secondLength).trimmed();
+ second = text.mid(equalsPosition + 1, secondLength).trimmed().toByteArray();
position = semiColonPosition;
return qMakePair(first, second);
@@ -435,6 +429,53 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
*/
/*!
+ \enum QNetworkCookie::SameSite
+ \since 6.1
+
+ \value Default SameSite is not set. Can be interpreted as None or Lax by the browser.
+ \value None Cookies can be sent in all contexts. This used to be default, but
+ recent browsers made Lax default, and will now require the cookie to be both secure and to set SameSite=None.
+ \value Lax Cookies are sent on first party requests and GET requests initiated by third party website.
+ This is the default in modern browsers (since mid 2020).
+ \value Strict Cookies will only be sent in a first-party context.
+
+ \sa setSameSitePolicy(), sameSitePolicy()
+*/
+
+namespace {
+
+constexpr QByteArrayView sameSiteNone() noexcept { return "None"; }
+constexpr QByteArrayView sameSiteLax() noexcept { return "Lax"; }
+constexpr QByteArrayView sameSiteStrict() noexcept { return "Strict"; }
+
+QByteArrayView sameSiteToRawString(QNetworkCookie::SameSite samesite) noexcept
+{
+ switch (samesite) {
+ case QNetworkCookie::SameSite::None:
+ return sameSiteNone();
+ case QNetworkCookie::SameSite::Lax:
+ return sameSiteLax();
+ case QNetworkCookie::SameSite::Strict:
+ return sameSiteStrict();
+ case QNetworkCookie::SameSite::Default:
+ break;
+ }
+ return QByteArrayView();
+}
+
+QNetworkCookie::SameSite sameSiteFromRawString(QByteArrayView str) noexcept
+{
+ if (str.compare(sameSiteNone(), Qt::CaseInsensitive) == 0)
+ return QNetworkCookie::SameSite::None;
+ if (str.compare(sameSiteLax(), Qt::CaseInsensitive) == 0)
+ return QNetworkCookie::SameSite::Lax;
+ if (str.compare(sameSiteStrict(), Qt::CaseInsensitive) == 0)
+ return QNetworkCookie::SameSite::Strict;
+ return QNetworkCookie::SameSite::Default;
+}
+} // namespace
+
+/*!
Returns the raw form of this QNetworkCookie. The QByteArray
returned by this function is suitable for an HTTP header, either
in a server response (the Set-Cookie header) or the client request
@@ -459,14 +500,18 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
result += "; secure";
if (isHttpOnly())
result += "; HttpOnly";
+ if (d->sameSite != SameSite::Default) {
+ result += "; SameSite=";
+ result += sameSiteToRawString(d->sameSite);
+ }
if (!isSessionCookie()) {
result += "; expires=";
result += QLocale::c().toString(d->expirationDate.toUTC(),
- QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
+ "ddd, dd-MMM-yyyy hh:mm:ss 'GMT"_L1).toLatin1();
}
if (!d->domain.isEmpty()) {
result += "; domain=";
- if (d->domain.startsWith(QLatin1Char('.'))) {
+ if (d->domain.startsWith(u'.')) {
result += '.';
result += QUrl::toAce(d->domain.mid(1));
} else {
@@ -535,11 +580,11 @@ static inline bool isValueSeparator(char c)
static inline bool isWhitespace(char c)
{ return c == ' ' || c == '\t'; }
-static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
+static bool checkStaticArray(int &val, QByteArrayView dateString, int at, const char *array, int size)
{
if (dateString[at] < 'a' || dateString[at] > 'z')
return false;
- if (val == -1 && dateString.length() >= at + 3) {
+ if (val == -1 && dateString.size() >= at + 3) {
int j = 0;
int i = 0;
while (i <= size) {
@@ -582,7 +627,7 @@ static bool checkStaticArray(int &val, const QByteArray &dateString, int at, con
Or in their own words:
"} // else what the hell is this."
*/
-static QDateTime parseDateString(const QByteArray &dateString)
+static QDateTime parseDateString(QByteArrayView dateString)
{
QTime time;
// placeholders for values when we are not sure it is a year, month or day
@@ -593,10 +638,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
int zoneOffset = -1;
// hour:minute:second.ms pm
- QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
+ static const QRegularExpression timeRx(
+ u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_s);
int at = 0;
- while (at < dateString.length()) {
+ while (at < dateString.size()) {
#ifdef PARSEDATESTRINGDEBUG
qDebug() << dateString.mid(at);
#endif
@@ -637,20 +683,20 @@ static QDateTime parseDateString(const QByteArray &dateString)
&& (dateString[at - 1] == 't')))) {
int end = 1;
- while (end < 5 && dateString.length() > at+end
+ while (end < 5 && dateString.size() > at+end
&& dateString[at + end] >= '0' && dateString[at + end] <= '9')
++end;
int minutes = 0;
int hours = 0;
switch (end - 1) {
case 4:
- minutes = atoi(dateString.mid(at + 3, 2).constData());
+ minutes = dateString.mid(at + 3, 2).toInt();
Q_FALLTHROUGH();
case 2:
- hours = atoi(dateString.mid(at + 1, 2).constData());
+ hours = dateString.mid(at + 1, 2).toInt();
break;
case 1:
- hours = atoi(dateString.mid(at + 1, 1).constData());
+ hours = dateString.mid(at + 1, 1).toInt();
break;
default:
at += end;
@@ -669,25 +715,27 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Time
if (isNum && time.isNull()
- && dateString.length() >= at + 3
+ && dateString.size() >= at + 3
&& (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
// While the date can be found all over the string the format
// for the time is set and a nice regexp can be used.
- int pos = timeRx.indexIn(QLatin1String(dateString), at);
- if (pos != -1) {
- QStringList list = timeRx.capturedTexts();
- int h = atoi(list.at(1).toLatin1().constData());
- int m = atoi(list.at(2).toLatin1().constData());
- int s = atoi(list.at(4).toLatin1().constData());
- int ms = atoi(list.at(6).toLatin1().constData());
- if (h < 12 && !list.at(9).isEmpty())
- if (list.at(9) == QLatin1String("pm"))
+ // This string needs to stay for as long as the QRegularExpressionMatch is used,
+ // or else we get use-after-free issues:
+ QString dateToString = QString::fromLatin1(dateString);
+ if (auto match = timeRx.match(dateToString, at); match.hasMatch()) {
+ int h = match.capturedView(1).toInt();
+ int m = match.capturedView(2).toInt();
+ int s = match.capturedView(3).toInt();
+ int ms = match.capturedView(4).toInt();
+ QStringView ampm = match.capturedView(5);
+ if (h < 12 && !ampm.isEmpty())
+ if (ampm == "pm"_L1)
h += 12;
time = QTime(h, m, s, ms);
#ifdef PARSEDATESTRINGDEBUG
- qDebug() << "Time:" << list << timeRx.matchedLength();
+ qDebug() << "Time:" << match.capturedTexts() << match.capturedLength();
#endif
- at += timeRx.matchedLength();
+ at += match.capturedLength();
continue;
}
}
@@ -695,11 +743,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
// 4 digit Year
if (isNum
&& year == -1
- && dateString.length() > at + 3) {
+ && dateString.size() > at + 3) {
if (isNumber(dateString[at + 1])
&& isNumber(dateString[at + 2])
&& isNumber(dateString[at + 3])) {
- year = atoi(dateString.mid(at, 4).constData());
+ year = dateString.mid(at, 4).toInt();
at += 4;
#ifdef PARSEDATESTRINGDEBUG
qDebug() << "Year:" << year;
@@ -712,10 +760,10 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Could be month, day or year
if (isNum) {
int length = 1;
- if (dateString.length() > at + 1
+ if (dateString.size() > at + 1
&& isNumber(dateString[at + 1]))
++length;
- int x = atoi(dateString.mid(at, length).constData());
+ int x = dateString.mid(at, length).toInt();
if (year == -1 && (x > 31 || x == 0)) {
year = x;
} else {
@@ -864,11 +912,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
if (!date.isValid())
date = QDate(day + y2k, month, year);
- QDateTime dateTime(date, time, Qt::UTC);
+ QDateTime dateTime(date, time, QTimeZone::UTC);
- if (zoneOffset != -1) {
+ if (zoneOffset != -1)
dateTime = dateTime.addSecs(zoneOffset);
- }
+
if (!dateTime.isValid())
return QDateTime();
return dateTime;
@@ -884,19 +932,19 @@ static QDateTime parseDateString(const QByteArray &dateString)
cookie that is parsed.
\sa toRawForm()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookie::parseCookies(QByteArrayView cookieString)
{
// cookieString can be a number of set-cookie header strings joined together
// by \n, parse each line separately.
QList<QNetworkCookie> cookies;
- QList<QByteArray> list = cookieString.split('\n');
- for (int a = 0; a < list.size(); a++)
- cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
+ for (auto s : QLatin1StringView(cookieString).tokenize('\n'_L1))
+ cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(s);
return cookies;
}
-QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(QByteArrayView cookieString)
{
// According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
// the Set-Cookie response header is of the format:
@@ -911,7 +959,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
const QDateTime now = QDateTime::currentDateTimeUtc();
int position = 0;
- const int length = cookieString.length();
+ const int length = cookieString.size();
while (position < length) {
QNetworkCookie cookie;
@@ -929,28 +977,27 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
case ';':
// new field in the cookie
field = nextField(cookieString, position, false);
- field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
- if (field.first == "expires") {
- position -= field.second.length();
+ if (field.first.compare("expires", Qt::CaseInsensitive) == 0) {
+ position -= field.second.size();
int end;
for (end = position; end < length; ++end)
if (isValueSeparator(cookieString.at(end)))
break;
- QByteArray dateString = cookieString.mid(position, end - position).trimmed();
+ QByteArray dateString = cookieString.mid(position, end - position).trimmed().toByteArray().toLower();
position = end;
- QDateTime dt = parseDateString(dateString.toLower());
+ QDateTime dt = parseDateString(dateString);
if (dt.isValid())
cookie.setExpirationDate(dt);
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.1)
- } else if (field.first == "domain") {
- QByteArray rawDomain = field.second;
+ } else if (field.first.compare("domain", Qt::CaseInsensitive) == 0) {
+ QByteArrayView rawDomain = field.second;
//empty domain should be ignored (RFC6265 section 5.2.3)
if (!rawDomain.isEmpty()) {
- QString maybeLeadingDot;
+ QLatin1StringView maybeLeadingDot;
if (rawDomain.startsWith('.')) {
- maybeLeadingDot = QLatin1Char('.');
+ maybeLeadingDot = "."_L1;
rawDomain = rawDomain.mid(1);
}
@@ -965,7 +1012,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
return result;
}
}
- } else if (field.first == "max-age") {
+ } else if (field.first.compare("max-age", Qt::CaseInsensitive) == 0) {
bool ok = false;
int secs = field.second.toInt(&ok);
if (ok) {
@@ -977,7 +1024,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
}
}
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.2)
- } else if (field.first == "path") {
+ } else if (field.first.compare("path", Qt::CaseInsensitive) == 0) {
if (field.second.startsWith('/')) {
// ### we should treat cookie paths as an octet sequence internally
// However RFC6265 says we should assume UTF-8 for presentation as a string
@@ -987,10 +1034,12 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
// and also IETF test case path0030 which has valid and empty path in the same cookie
cookie.setPath(QString());
}
- } else if (field.first == "secure") {
+ } else if (field.first.compare("secure", Qt::CaseInsensitive) == 0) {
cookie.setSecure(true);
- } else if (field.first == "httponly") {
+ } else if (field.first.compare("httponly", Qt::CaseInsensitive) == 0) {
cookie.setHttpOnly(true);
+ } else if (field.first.compare("samesite", Qt::CaseInsensitive) == 0) {
+ cookie.setSameSitePolicy(sameSiteFromRawString(field.second));
} else {
// ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
}
@@ -1016,9 +1065,9 @@ void QNetworkCookie::normalize(const QUrl &url)
// don't do path checking. See QTBUG-5815
if (d->path.isEmpty()) {
QString pathAndFileName = url.path();
- QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
+ QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(u'/') + 1);
if (defaultPath.isEmpty())
- defaultPath = QLatin1Char('/');
+ defaultPath = u'/';
d->path = defaultPath;
}
@@ -1028,12 +1077,12 @@ void QNetworkCookie::normalize(const QUrl &url)
QHostAddress hostAddress(d->domain);
if (hostAddress.protocol() != QAbstractSocket::IPv4Protocol
&& hostAddress.protocol() != QAbstractSocket::IPv6Protocol
- && !d->domain.startsWith(QLatin1Char('.'))) {
+ && !d->domain.startsWith(u'.')) {
// Ensure the domain starts with a dot if its field was not empty
// in the HTTP header. There are some servers that forget the
// leading dot and this is actually forbidden according to RFC 2109,
// but all browsers accept it anyway so we do that as well.
- d->domain.prepend(QLatin1Char('.'));
+ d->domain.prepend(u'.');
}
}
}
@@ -1049,3 +1098,5 @@ QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
#endif
QT_END_NAMESPACE
+
+#include "moc_qnetworkcookie.cpp"
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index b712b63849..aed9c8af12 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIE_H
#define QNETWORKCOOKIE_H
@@ -57,11 +21,19 @@ class QUrl;
class QNetworkCookiePrivate;
class Q_NETWORK_EXPORT QNetworkCookie
{
+ Q_GADGET
public:
enum RawForm {
NameAndValueOnly,
Full
};
+ enum class SameSite {
+ Default,
+ None,
+ Lax,
+ Strict
+ };
+ Q_ENUM(SameSite)
explicit QNetworkCookie(const QByteArray &name = QByteArray(), const QByteArray &value = QByteArray());
QNetworkCookie(const QNetworkCookie &other);
@@ -69,7 +41,7 @@ public:
QNetworkCookie &operator=(QNetworkCookie &&other) noexcept { swap(other); return *this; }
QNetworkCookie &operator=(const QNetworkCookie &other);
- void swap(QNetworkCookie &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkCookie &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkCookie &other) const;
inline bool operator!=(const QNetworkCookie &other) const
@@ -79,6 +51,8 @@ public:
void setSecure(bool enable);
bool isHttpOnly() const;
void setHttpOnly(bool enable);
+ SameSite sameSitePolicy() const;
+ void setSameSitePolicy(SameSite sameSite);
bool isSessionCookie() const;
QDateTime expirationDate() const;
@@ -101,7 +75,10 @@ public:
bool hasSameIdentifier(const QNetworkCookie &other) const;
void normalize(const QUrl &url);
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static QList<QNetworkCookie> parseCookies(const QByteArray &cookieString);
+#endif
+ static QList<QNetworkCookie> parseCookies(QByteArrayView cookieString);
private:
QSharedDataPointer<QNetworkCookiePrivate> d;
@@ -117,6 +94,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QNetworkCookie &);
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkCookie)
+QT_DECL_METATYPE_EXTERN(QNetworkCookie, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
index 13538ad243..ce4378fd64 100644
--- a/src/network/access/qnetworkcookie_p.h
+++ b/src/network/access/qnetworkcookie_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIE_P_H
#define QNETWORKCOOKIE_P_H
@@ -53,14 +17,15 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qdatetime.h"
+#include "QtNetwork/qnetworkcookie.h"
QT_BEGIN_NAMESPACE
class QNetworkCookiePrivate: public QSharedData
{
public:
- inline QNetworkCookiePrivate() : secure(false), httpOnly(false) { }
- static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
+ QNetworkCookiePrivate() = default;
+ static QList<QNetworkCookie> parseSetCookieHeaderLine(QByteArrayView cookieString);
QDateTime expirationDate;
QString domain;
@@ -68,8 +33,9 @@ public:
QString comment;
QByteArray name;
QByteArray value;
- bool secure;
- bool httpOnly;
+ QNetworkCookie::SameSite sameSite = QNetworkCookie::SameSite::Default;
+ bool secure = false;
+ bool httpOnly = false;
};
static inline bool isLWS(char c)
@@ -77,13 +43,13 @@ static inline bool isLWS(char c)
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
-static int nextNonWhitespace(const QByteArray &text, int from)
+static int nextNonWhitespace(QByteArrayView text, int from)
{
// RFC 2616 defines linear whitespace as:
// LWS = [CRLF] 1*( SP | HT )
// We ignore the fact that CRLF must come as a pair at this point
// It's an invalid HTTP header if that happens.
- while (from < text.length()) {
+ while (from < text.size()) {
if (isLWS(text.at(from)))
++from;
else
@@ -91,7 +57,7 @@ static int nextNonWhitespace(const QByteArray &text, int from)
}
// reached the end
- return text.length();
+ return text.size();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
index af5b126953..82746f91b1 100644
--- a/src/network/access/qnetworkcookiejar.cpp
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkcookiejar.h"
#include "qnetworkcookiejar_p.h"
@@ -47,16 +11,18 @@
#include "private/qtldurl_p.h"
#else
QT_BEGIN_NAMESPACE
-static bool qIsEffectiveTLD(QString domain)
+static bool qIsEffectiveTLD(QStringView domain)
{
// provide minimal checking by not accepting cookies on real TLDs
- return !domain.contains(QLatin1Char('.'));
+ return !domain.contains(u'.');
}
QT_END_NAMESPACE
#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QNetworkCookieJar
\since 4.4
@@ -146,31 +112,31 @@ void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
d->allCookies = cookieList;
}
-static inline bool isParentPath(const QString &path, const QString &reference)
+static inline bool isParentPath(QStringView path, QStringView reference)
{
- if ((path.isEmpty() && reference == QLatin1String("/")) || path.startsWith(reference)) {
+ if ((path.isEmpty() && reference == "/"_L1) || path.startsWith(reference)) {
//The cookie-path and the request-path are identical.
- if (path.length() == reference.length())
+ if (path.size() == reference.size())
return true;
//The cookie-path is a prefix of the request-path, and the last
//character of the cookie-path is %x2F ("/").
- if (reference.endsWith('/'))
+ if (reference.endsWith(u'/'))
return true;
//The cookie-path is a prefix of the request-path, and the first
//character of the request-path that is not included in the cookie-
//path is a %x2F ("/") character.
- if (path.at(reference.length()) == '/')
+ if (path.at(reference.size()) == u'/')
return true;
}
return false;
}
-static inline bool isParentDomain(const QString &domain, const QString &reference)
+static inline bool isParentDomain(QStringView domain, QStringView reference)
{
- if (!reference.startsWith(QLatin1Char('.')))
+ if (!reference.startsWith(u'.'))
return domain == reference;
- return domain.endsWith(reference) || domain == reference.midRef(1);
+ return domain.endsWith(reference) || domain == reference.mid(1);
}
/*!
@@ -234,49 +200,38 @@ QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
Q_D(const QNetworkCookieJar);
const QDateTime now = QDateTime::currentDateTimeUtc();
QList<QNetworkCookie> result;
- bool isEncrypted = url.scheme() == QLatin1String("https");
+ const bool isEncrypted = url.scheme() == "https"_L1;
// scan our cookies for something that matches
- QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
- end = d->allCookies.constEnd();
- for ( ; it != end; ++it) {
- if (!isParentDomain(url.host(), it->domain()))
+ for (const auto &cookie : std::as_const(d->allCookies)) {
+ if (!isEncrypted && cookie.isSecure())
continue;
- if (!isParentPath(url.path(), it->path()))
+ if (!cookie.isSessionCookie() && cookie.expirationDate() < now)
continue;
- if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
+ const QString urlHost = url.host();
+ const QString cookieDomain = cookie.domain();
+ if (!isParentDomain(urlHost, cookieDomain))
continue;
- if ((*it).isSecure() && !isEncrypted)
+ if (!isParentPath(url.path(), cookie.path()))
continue;
- QString domain = it->domain();
- if (domain.startsWith(QLatin1Char('.'))) /// Qt6?: remove when compliant with RFC6265
- domain = domain.mid(1);
+ QStringView domain = cookieDomain;
+ if (domain.startsWith(u'.')) /// Qt6?: remove when compliant with RFC6265
+ domain = domain.sliced(1);
#if QT_CONFIG(topleveldomain)
- if (qIsEffectiveTLD(domain) && url.host() != domain)
+ if (urlHost != domain && qIsEffectiveTLD(domain))
continue;
#else
- if (!domain.contains(QLatin1Char('.')) && url.host() != domain)
+ if (!domain.contains(u'.') && urlHost != domain)
continue;
#endif // topleveldomain
- // insert this cookie into result, sorted by path
- QList<QNetworkCookie>::Iterator insertIt = result.begin();
- while (insertIt != result.end()) {
- if (insertIt->path().length() < it->path().length()) {
- // insert here
- insertIt = result.insert(insertIt, *it);
- break;
- } else {
- ++insertIt;
- }
- }
-
- // this is the shortest path yet, just append
- if (insertIt == result.end())
- result += *it;
+ result += cookie;
}
+ auto longerPath = [](const auto &c1, const auto &c2)
+ { return c1.path().size() > c2.path().size(); };
+ std::sort(result.begin(), result.end(), longerPath);
return result;
}
@@ -333,12 +288,11 @@ bool QNetworkCookieJar::updateCookie(const QNetworkCookie &cookie)
bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
{
Q_D(QNetworkCookieJar);
- QList<QNetworkCookie>::Iterator it;
- for (it = d->allCookies.begin(); it != d->allCookies.end(); ++it) {
- if (it->hasSameIdentifier(cookie)) {
- d->allCookies.erase(it);
- return true;
- }
+ const auto it = std::find_if(d->allCookies.cbegin(), d->allCookies.cend(),
+ [&cookie](const auto &c) { return c.hasSameIdentifier(cookie); });
+ if (it != d->allCookies.cend()) {
+ d->allCookies.erase(it);
+ return true;
}
return false;
}
@@ -351,13 +305,14 @@ bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
*/
bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl &url) const
{
- QString domain = cookie.domain();
+ const QString cookieDomain = cookie.domain();
+ QStringView domain = cookieDomain;
const QString host = url.host();
if (!isParentDomain(domain, host) && !isParentDomain(host, domain))
return false; // not accepted
- if (domain.startsWith(QLatin1Char('.')))
- domain = domain.mid(1);
+ if (domain.startsWith(u'.'))
+ domain = domain.sliced(1);
// We shouldn't reject if:
// "[...] the domain-attribute is identical to the canonicalized request-host"
@@ -373,3 +328,5 @@ bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkcookiejar.cpp"
diff --git a/src/network/access/qnetworkcookiejar.h b/src/network/access/qnetworkcookiejar.h
index c3b2200443..71f2884a28 100644
--- a/src/network/access/qnetworkcookiejar.h
+++ b/src/network/access/qnetworkcookiejar.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIEJAR_H
#define QNETWORKCOOKIEJAR_H
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
index 43f189a40c..f2837ab83d 100644
--- a/src/network/access/qnetworkcookiejar_p.h
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIEJAR_P_H
#define QNETWORKCOOKIEJAR_P_H
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index b30d1c9664..b39924025e 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKDISKCACHE_DEBUG
@@ -48,20 +12,23 @@
#include <qdir.h>
#include <qdatastream.h>
#include <qdatetime.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
#include <qurl.h>
#include <qcryptographichash.h>
#include <qdebug.h>
-#define CACHE_POSTFIX QLatin1String(".d")
-#define PREPARED_SLASH QLatin1String("prepared/")
+#include <memory>
+
+#define CACHE_POSTFIX ".d"_L1
#define CACHE_VERSION 8
-#define DATA_DIR QLatin1String("data")
+#define DATA_DIR "data"_L1
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QNetworkDiskCache
\since 4.5
@@ -132,7 +99,7 @@ QString QNetworkDiskCache::cacheDirectory() const
Prepared cache items will be stored in the new cache directory when
they are inserted.
- \sa QDesktopServices::CacheLocation
+ \sa QStandardPaths::CacheLocation
*/
void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
{
@@ -145,10 +112,10 @@ void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
d->cacheDirectory = cacheDir;
QDir dir(d->cacheDirectory);
d->cacheDirectory = dir.absolutePath();
- if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
- d->cacheDirectory += QLatin1Char('/');
+ if (!d->cacheDirectory.endsWith(u'/'))
+ d->cacheDirectory += u'/';
- d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/');
+ d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + u'/';
d->prepareLayout();
}
@@ -187,16 +154,12 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
return nullptr;
}
- const auto headers = metaData.rawHeaders();
- for (const auto &header : headers) {
- if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) {
- const qint64 size = header.second.toLongLong();
- if (size > (maximumCacheSize() * 3)/4)
- return nullptr;
- break;
- }
- }
- QScopedPointer<QCacheItem> cacheItem(new QCacheItem);
+ const auto sizeValue = metaData.headers().value(QHttpHeaders::WellKnownHeader::ContentLength);
+ const qint64 size = sizeValue.toLongLong();
+ if (size > (maximumCacheSize() * 3)/4)
+ return nullptr;
+
+ std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
cacheItem->metaData = metaData;
QIODevice *device = nullptr;
@@ -204,13 +167,9 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->data.open(QBuffer::ReadWrite);
device = &(cacheItem->data);
} else {
- QString templateName = d->tmpCacheFileName();
- QT_TRY {
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- } QT_CATCH(...) {
- cacheItem->file = nullptr;
- }
- if (!cacheItem->file || !cacheItem->file->open()) {
+ QString fileName = d->cacheFileName(cacheItem->metaData.url());
+ cacheItem->file = new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
+ if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
qWarning("QNetworkDiskCache::prepare() unable to open temporary file");
cacheItem.reset();
return nullptr;
@@ -218,7 +177,7 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->writeHeader(cacheItem->file);
device = cacheItem->file;
}
- d->inserting[device] = cacheItem.take();
+ d->inserting[device] = cacheItem.release();
return device;
}
@@ -250,7 +209,6 @@ void QNetworkDiskCache::insert(QIODevice *device)
void QNetworkDiskCachePrivate::prepareLayout()
{
QDir helper;
- helper.mkpath(cacheDirectory + PREPARED_SLASH);
//Create directory and subdirectories 0-F
helper.mkpath(dataDirectory);
@@ -271,19 +229,16 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
Q_ASSERT(!fileName.isEmpty());
if (QFile::exists(fileName)) {
- if (!QFile::remove(fileName)) {
+ if (!removeFile(fileName)) {
qWarning() << "QNetworkDiskCache: couldn't remove the cache file " << fileName;
return;
}
}
- if (currentCacheSize > 0)
- currentCacheSize += 1024 + cacheItem->size();
currentCacheSize = q->expire();
if (!cacheItem->file) {
- QString templateName = tmpCacheFileName();
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- if (cacheItem->file->open()) {
+ cacheItem->file = new QSaveFile(fileName, &cacheItem->data);
+ if (cacheItem->file->open(QFileDevice::WriteOnly)) {
cacheItem->writeHeader(cacheItem->file);
cacheItem->writeCompressedData(cacheItem->file);
}
@@ -291,13 +246,15 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
if (cacheItem->file
&& cacheItem->file->isOpen()
- && cacheItem->file->error() == QFile::NoError) {
- cacheItem->file->setAutoRemove(false);
- // ### use atomic rename rather then remove & rename
- if (cacheItem->file->rename(fileName))
- currentCacheSize += cacheItem->file->size();
- else
- cacheItem->file->setAutoRemove(true);
+ && cacheItem->file->error() == QFileDevice::NoError) {
+ // We have to call size() here instead of inside the if-body because
+ // commit() invalidates the file-engine, and size() will create a new
+ // one, pointing at an empty filename.
+ qint64 size = cacheItem->file->size();
+ if (cacheItem->file->commit())
+ currentCacheSize += size;
+ // Delete and unset the QSaveFile, it's invalid now.
+ delete std::exchange(cacheItem->file, nullptr);
}
if (cacheItem->metaData.url() == lastItem.metaData.url())
lastItem.reset();
@@ -395,7 +352,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
qDebug() << "QNetworkDiskCache::data()" << url;
#endif
Q_D(QNetworkDiskCache);
- QScopedPointer<QBuffer> buffer;
+ std::unique_ptr<QBuffer> buffer;
if (!url.isValid())
return nullptr;
if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
@@ -417,22 +374,11 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
buffer->setData(d->lastItem.data.data());
} else {
buffer.reset(new QBuffer);
- // ### verify that QFile uses the fd size and not the file name
- qint64 size = file->size() - file->pos();
- const uchar *p = nullptr;
-#if !defined(Q_OS_INTEGRITY)
- p = file->map(file->pos(), size);
-#endif
- if (p) {
- buffer->setData((const char *)p, size);
- file.take()->setParent(buffer.data());
- } else {
- buffer->setData(file->readAll());
- }
+ buffer->setData(file->readAll());
}
}
buffer->open(QBuffer::ReadOnly);
- return buffer.take();
+ return buffer.release();
}
/*!
@@ -527,47 +473,45 @@ qint64 QNetworkDiskCache::expire()
// close file handle to prevent "in use" error when QFile::remove() is called
d->lastItem.reset();
- QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
+ const QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QMultiMap<QDateTime, QString> cacheItems;
+ struct CacheItem
+ {
+ std::chrono::milliseconds msecs;
+ QString path;
+ qint64 size = 0;
+ };
+ std::vector<CacheItem> cacheItems;
qint64 totalSize = 0;
- while (it.hasNext()) {
- QString path = it.next();
- QFileInfo info = it.fileInfo();
- QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX)) {
- const QDateTime birthTime = info.fileTime(QFile::FileBirthTime);
- cacheItems.insert(birthTime.isValid() ? birthTime
- : info.fileTime(QFile::FileMetadataChangeTime), path);
- totalSize += info.size();
- }
+ using F = QDirListing::IteratorFlag;
+ for (const auto &dirEntry : QDirListing(cacheDirectory(), filters, F::Recursive)) {
+ if (!dirEntry.fileName().endsWith(CACHE_POSTFIX))
+ continue;
+
+ const QFileInfo &info = dirEntry.fileInfo();
+ QDateTime fileTime = info.birthTime(QTimeZone::UTC);
+ if (!fileTime.isValid())
+ fileTime = info.metadataChangeTime(QTimeZone::UTC);
+ const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
+ const qint64 size = info.size();
+ cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
+ totalSize += size;
}
- int removedFiles = 0;
- qint64 goal = (maximumCacheSize() * 9) / 10;
- QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
- while (i != cacheItems.constEnd()) {
- if (totalSize < goal)
- break;
- QString name = i.value();
- QFile file(name);
-
- if (name.contains(PREPARED_SLASH)) {
- for (QCacheItem *item : qAsConst(d->inserting)) {
- if (item && item->file && item->file->fileName() == name) {
- delete item->file;
- item->file = nullptr;
- break;
- }
- }
- }
+ const qint64 goal = (maximumCacheSize() * 9) / 10;
+ if (totalSize < goal)
+ return totalSize; // Nothing to do
+
+ auto byFileTime = [&](const auto &a, const auto &b) { return a.msecs < b.msecs; };
+ std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
- qint64 size = file.size();
- file.remove();
- totalSize -= size;
+ [[maybe_unused]] int removedFiles = 0; // used under QNETWORKDISKCACHE_DEBUG
+ for (const CacheItem &cached : cacheItems) {
+ QFile::remove(cached.path);
++removedFiles;
- ++i;
+ totalSize -= cached.size;
+ if (totalSize < goal)
+ break;
}
#if defined(QNETWORKDISKCACHE_DEBUG)
if (removedFiles > 0) {
@@ -603,24 +547,16 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
cleanUrl.setPassword(QString());
cleanUrl.setFragment(QString());
- QCryptographicHash hash(QCryptographicHash::Sha1);
- hash.addData(cleanUrl.toEncoded());
+ const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
// convert sha1 to base36 form and return first 8 bytes for use as string
- const QByteArray id = QByteArray::number(*(qlonglong*)hash.result().constData(), 36).left(8);
- // generates <one-char subdir>/<8-char filname.d>
- uint code = (uint)id.at(id.length()-1) % 16;
- QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
- + QLatin1String(id) + CACHE_POSTFIX;
+ const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
+ // generates <one-char subdir>/<8-char filename.d>
+ uint code = (uint)id.at(id.size()-1) % 16;
+ QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) + CACHE_POSTFIX;
return pathFragment;
}
-QString QNetworkDiskCachePrivate::tmpCacheFileName() const
-{
- //The subdirectory is presumed to be already read for use.
- return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
-}
-
/*!
Generates fully qualified path of cached resource from a URL.
*/
@@ -638,31 +574,27 @@ QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const
*/
bool QCacheItem::canCompress() const
{
- bool sizeOk = false;
- bool typeOk = false;
- const auto headers = metaData.rawHeaders();
- for (const auto &header : headers) {
- if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) {
- qint64 size = header.second.toLongLong();
- if (size > MAX_COMPRESSION_SIZE)
- return false;
- else
- sizeOk = true;
- }
+ const auto h = metaData.headers();
- if (header.first.compare("content-type", Qt::CaseInsensitive) == 0) {
- QByteArray type = header.second;
- if (type.startsWith("text/")
- || (type.startsWith("application/")
- && (type.endsWith("javascript") || type.endsWith("ecmascript"))))
- typeOk = true;
- else
- return false;
- }
- if (sizeOk && typeOk)
- return true;
+ const auto sizeValue = h.value(QHttpHeaders::WellKnownHeader::ContentLength);
+ if (sizeValue.empty())
+ return false;
+
+ qint64 size = sizeValue.toLongLong();
+ if (size > MAX_COMPRESSION_SIZE)
+ return false;
+
+ const auto type = h.value(QHttpHeaders::WellKnownHeader::ContentType);
+ if (!type.empty())
+ return false;
+
+ if (!type.startsWith("text/")
+ && !(type.startsWith("application/")
+ && (type.endsWith("javascript") || type.endsWith("ecmascript")))) {
+ return false;
}
- return false;
+
+ return true;
}
enum
@@ -671,7 +603,7 @@ enum
CurrentCacheVersion = CACHE_VERSION
};
-void QCacheItem::writeHeader(QFile *device) const
+void QCacheItem::writeHeader(QFileDevice *device) const
{
QDataStream out(device);
@@ -683,7 +615,7 @@ void QCacheItem::writeHeader(QFile *device) const
out << compressed;
}
-void QCacheItem::writeCompressedData(QFile *device) const
+void QCacheItem::writeCompressedData(QFileDevice *device) const
{
QDataStream out(device);
@@ -694,7 +626,7 @@ void QCacheItem::writeCompressedData(QFile *device) const
Returns \c false if the file is a cache file,
but is an older version and should be removed otherwise true.
*/
-bool QCacheItem::read(QFile *device, bool readData)
+bool QCacheItem::read(QFileDevice *device, bool readData)
{
reset();
@@ -733,7 +665,9 @@ bool QCacheItem::read(QFile *device, bool readData)
if (!device->fileName().endsWith(expectedFilename))
return false;
- return metaData.isValid();
+ return metaData.isValid() && !metaData.headers().isEmpty();
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkdiskcache.cpp"
diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h
index ff7d3192e8..370f3d2b26 100644
--- a/src/network/access/qnetworkdiskcache.h
+++ b/src/network/access/qnetworkdiskcache.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDISKCACHE_H
#define QNETWORKDISKCACHE_H
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index c797e63830..826f0a1d7d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDISKCACHE_P_H
#define QNETWORKDISKCACHE_P_H
@@ -56,20 +20,16 @@
#include <qbuffer.h>
#include <qhash.h>
-#include <qtemporaryfile.h>
+#include <qsavefile.h>
QT_REQUIRE_CONFIG(networkdiskcache);
QT_BEGIN_NAMESPACE
-class QFile;
-
class QCacheItem
{
public:
- QCacheItem() : file(nullptr)
- {
- }
+ QCacheItem() = default;
~QCacheItem()
{
reset();
@@ -77,7 +37,7 @@ public:
QNetworkCacheMetaData metaData;
QBuffer data;
- QTemporaryFile *file;
+ QSaveFile *file = nullptr;
inline qint64 size() const
{ return file ? file->size() : data.size(); }
@@ -87,9 +47,9 @@ public:
delete file;
file = nullptr;
}
- void writeHeader(QFile *device) const;
- void writeCompressedData(QFile *device) const;
- bool read(QFile *device, bool readData);
+ void writeHeader(QFileDevice *device) const;
+ void writeCompressedData(QFileDevice *device) const;
+ bool read(QFileDevice *device, bool readData);
bool canCompress() const;
};
@@ -105,7 +65,6 @@ public:
static QString uniqueFileName(const QUrl &url);
QString cacheFileName(const QUrl &url) const;
- QString tmpCacheFileName() const;
bool removeFile(const QString &file);
void storeItem(QCacheItem *item);
void prepareLayout();
diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp
index b7c91f28d8..fb9ce8232d 100644
--- a/src/network/access/qnetworkfile.cpp
+++ b/src/network/access/qnetworkfile.cpp
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkfile_p.h"
+#include "qnetworkrequest_p.h"
#include <QtCore/QDebug>
#include <QNetworkReply>
@@ -67,8 +32,10 @@ void QNetworkFile::open()
"Cannot open %1: Path is a directory").arg(fileName());
emit error(QNetworkReply::ContentOperationNotPermittedError, msg);
} else {
- emit headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified()));
- emit headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size()));
+ emit headerRead(QHttpHeaders::WellKnownHeader::LastModified,
+ QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
+ emit headerRead(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(fi.size()));
opened = QFile::open(QIODevice::ReadOnly | QIODevice::Unbuffered);
if (!opened) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
@@ -90,3 +57,5 @@ void QNetworkFile::close()
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkfile_p.cpp"
diff --git a/src/network/access/qnetworkfile_p.h b/src/network/access/qnetworkfile_p.h
index e788308d82..9dcb63711e 100644
--- a/src/network/access/qnetworkfile_p.h
+++ b/src/network/access/qnetworkfile_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKFILE_H
#define QNETWORKFILE_H
@@ -71,7 +35,7 @@ public Q_SLOTS:
Q_SIGNALS:
void finished(bool ok);
- void headerRead(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void headerRead(QHttpHeaders::WellKnownHeader, const QByteArray &value);
void error(QNetworkReply::NetworkError error, const QString &message);
};
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index fb30bfd4f1..3301ae85d2 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -1,41 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
@@ -43,6 +9,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkReply::NetworkError, QNetworkReply__NetworkError)
+
const int QNetworkReplyPrivate::progressSignalInterval = 100;
QNetworkReplyPrivate::QNetworkReplyPrivate()
@@ -74,7 +42,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
itself.
QNetworkReply is a sequential-access QIODevice, which means that
- once data is read from the object, it no longer kept by the
+ once data is read from the object, it is no longer kept by the
device. It is therefore the application's responsibility to keep
this data if it needs to. Whenever more data is received from the
network and processed, the readyRead() signal is emitted.
@@ -91,7 +59,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
content.
\note Do not delete the object in the slot connected to the
- error() or finished() signal. Use deleteLater().
+ errorOccurred() or finished() signal. Use deleteLater().
\sa QNetworkRequest, QNetworkAccessManager
*/
@@ -219,6 +187,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
the server response was detected
\sa error()
+ \sa errorOccurred()
*/
/*!
@@ -295,13 +264,13 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
\fn void QNetworkReply::redirected(const QUrl &url)
\since 5.6
- This signal is emitted if the QNetworkRequest::FollowRedirectsAttribute was
+ This signal is emitted if the QNetworkRequest::ManualRedirectPolicy was not
set in the request and the server responded with a 3xx status (specifically
301, 302, 303, 305, 307 or 308 status code) with a valid url in the location
header, indicating a HTTP redirect. The \a url parameter contains the new
redirect url as returned by the server in the location header.
- \sa QNetworkRequest::FollowRedirectsAttribute
+ \sa QNetworkRequest::RedirectPolicy
*/
/*!
@@ -319,6 +288,27 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
*/
/*!
+ \fn void QNetworkReply::socketStartedConnecting()
+ \since 6.3
+
+ This signal is emitted 0 or more times, when the socket
+ is connecting, before sending the request. Useful for
+ custom progress or timeout handling.
+
+ \sa metaDataChanged(), requestSent()
+*/
+
+/*!
+ \fn void QNetworkReply::requestSent()
+ \since 6.3
+
+ This signal is emitted 1 or more times when the request was
+ sent. Useful for custom progress or timeout handling.
+
+ \sa metaDataChanged(), socketStartedConnecting()
+*/
+
+/*!
\fn void QNetworkReply::metaDataChanged()
\omit FIXME: Update name? \endomit
@@ -341,7 +331,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
processing. After this signal is emitted, there will be no more
updates to the reply's data or metadata.
- Unless close() or abort() have been called, the reply will be still be opened
+ Unless close() or abort() have been called, the reply will still be opened
for reading, so the data can be retrieved by calls to read() or
readAll(). In particular, if no calls to read() were made as a
result of readyRead(), a call to readAll() will retrieve the full
@@ -361,7 +351,8 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
*/
/*!
- \fn void QNetworkReply::error(QNetworkReply::NetworkError code)
+ \fn void QNetworkReply::errorOccurred(QNetworkReply::NetworkError code)
+ \since 5.15
This signal is emitted when the reply detects an error in
processing. The finished() signal will probably follow, indicating
@@ -442,7 +433,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
QNetworkAccessManager functions to do that.
*/
QNetworkReply::QNetworkReply(QObject *parent)
- : QIODevice(*new QNetworkReplyPrivate, parent)
+ : QNetworkReply(*new QNetworkReplyPrivate, parent)
{
}
@@ -592,10 +583,10 @@ bool QNetworkReply::isRunning() const
/*!
Returns the URL of the content downloaded or uploaded. Note that
- the URL may be different from that of the original request. If the
- QNetworkRequest::FollowRedirectsAttribute was set in the request, then this
+ the URL may be different from that of the original request.
+ If redirections were enabled in the request, then this
function returns the current url that the network API is accessing, i.e the
- url emitted in the QNetworkReply::redirected signal.
+ url of the resource the request got redirected to.
\sa request(), setUrl(), QNetworkRequest::url(), redirected()
*/
@@ -621,11 +612,12 @@ QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
the remote server
\sa rawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkReply::hasRawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+ return d->headers().contains(headerName);
}
/*!
@@ -636,15 +628,12 @@ bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
header field.
\sa setRawHeader(), hasRawHeader(), header()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkReply::rawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->rawHeader(headerName);
}
/*! \typedef QNetworkReply::RawHeaderPair
@@ -659,7 +648,20 @@ QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
const QList<QNetworkReply::RawHeaderPair>& QNetworkReply::rawHeaderPairs() const
{
Q_D(const QNetworkReply);
- return d->rawHeaders;
+ return d->allRawHeaders();
+}
+
+/*!
+ \since 6.8
+
+ Returns headers that were sent by the remote server.
+
+ \sa setHeaders(), QNetworkRequest::setAttribute(), QNetworkRequest::Attribute
+*/
+QHttpHeaders QNetworkReply::headers() const
+{
+ Q_D(const QNetworkReply);
+ return d->headers();
}
/*!
@@ -855,7 +857,7 @@ void QNetworkReply::setRequest(const QNetworkRequest &request)
Sets the error condition to be \a errorCode. The human-readable
message is set with \a errorString.
- Calling setError() does not emit the error(QNetworkReply::NetworkError)
+ Calling setError() does not emit the errorOccurred(QNetworkReply::NetworkError)
signal.
\sa error(), errorString()
@@ -897,6 +899,45 @@ void QNetworkReply::setUrl(const QUrl &url)
}
/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network reply, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, they will be
+ parsed and the corresponding parsed form will also be set.
+
+ \sa headers(), QNetworkRequest::KnownHeaders
+*/
+void QNetworkReply::setHeaders(const QHttpHeaders &newHeaders)
+{
+ Q_D(QNetworkReply);
+ d->setHeaders(newHeaders);
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkReply::setHeaders(QHttpHeaders &&newHeaders)
+{
+ Q_D(QNetworkReply);
+ d->setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \since 6.8
+
+ Sets the header \a name to be of value \a value. If \a
+ name was previously set, it is overridden.
+*/
+void QNetworkReply::setWellKnownHeader(QHttpHeaders::WellKnownHeader name, const QByteArray &value)
+{
+ Q_D(QNetworkReply);
+ d->setHeader(name, value);
+}
+
+/*!
Sets the known header \a header to be of value \a value. The
corresponding raw form of the header will be set as well.
@@ -942,3 +983,5 @@ void QNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreply.cpp"
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 4a402daa91..48ec0397c6 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLY_H
#define QNETWORKREPLY_H
@@ -133,12 +97,19 @@ public:
QVariant header(QNetworkRequest::KnownHeaders header) const;
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
const QList<RawHeaderPair>& rawHeaderPairs() const;
+ QHttpHeaders headers() const;
// attributes
QVariant attribute(QNetworkRequest::Attribute code) const;
@@ -154,9 +125,11 @@ public Q_SLOTS:
virtual void ignoreSslErrors();
Q_SIGNALS:
+ void socketStartedConnecting();
+ void requestSent();
void metaDataChanged();
void finished();
- void error(QNetworkReply::NetworkError);
+ void errorOccurred(QNetworkReply::NetworkError);
#if QT_CONFIG(ssl)
void encrypted();
void sslErrors(const QList<QSslError> &errors);
@@ -180,6 +153,9 @@ protected:
void setUrl(const QUrl &url);
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+ void setWellKnownHeader(QHttpHeaders::WellKnownHeader name, const QByteArray &value);
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
#if QT_CONFIG(ssl)
@@ -194,6 +170,7 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkReply::NetworkError)
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkReply::NetworkError,
+ QNetworkReply__NetworkError, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index 66d8c9d527..f9bbfcbc6b 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLY_P_H
#define QNETWORKREPLY_P_H
@@ -70,8 +34,6 @@ public:
Working, // The reply is uploading/downloading data.
Finished, // The reply has finished.
Aborted, // The reply has been aborted.
- WaitingForSession, // The reply is waiting for the session to open before connecting.
- Reconnecting // The reply will reconnect to once roaming has completed.
};
QNetworkReplyPrivate();
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
index 924c905b1a..006cfd57cb 100644
--- a/src/network/access/qnetworkreplydataimpl.cpp
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplydataimpl_p.h"
#include "private/qdataurl_p.h"
@@ -72,8 +36,11 @@ QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequ
QByteArray payload;
if (qDecodeDataUrl(url, mimeType, payload)) {
qint64 size = payload.size();
- setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
- setHeader(QNetworkRequest::ContentLengthHeader, size);
+ auto h = headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentType, mimeType);
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength, QByteArray::number(size));
+ setHeaders(std::move(h));
+
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
d->decodedData.setData(payload);
@@ -88,7 +55,7 @@ QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequ
const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
"Invalid URI: %1").arg(url.toString());
setError(QNetworkReply::ProtocolFailure, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolFailure));
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
diff --git a/src/network/access/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 81d2110d69..35fd38aece 100644
--- a/src/network/access/qnetworkreplydataimpl_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYDATAIMPL_H
#define QNETWORKREPLYDATAIMPL_H
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index afab8ffd94..bad0bb7b0a 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplyfileimpl_p.h"
@@ -49,6 +13,10 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders, QNetworkRequest__KnownHeaders)
+
QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
: QNetworkReplyPrivate(), managerPrivate(nullptr), realFile(nullptr)
{
@@ -80,7 +48,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
d->managerPrivate = manager->d_func();
QUrl url = req.url();
- if (url.host() == QLatin1String("localhost"))
+ if (url.host() == "localhost"_L1)
url.setHost(QString());
#if !defined(Q_OS_WIN)
@@ -89,25 +57,26 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
// we handle only local files
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ setFinished(true); // We're finished, will emit finished() after ctor is done.
+ QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
- fileOpenFinished(false);
+ QMetaObject::invokeMethod(this, &QNetworkReplyFileImpl::fileOpenFinished, Qt::QueuedConnection, false);
return;
}
#endif
if (url.path().isEmpty())
- url.setPath(QLatin1String("/"));
+ url.setPath("/"_L1);
setUrl(url);
QString fileName = url.toLocalFile();
if (fileName.isEmpty()) {
const QString scheme = url.scheme();
- if (scheme == QLatin1String("qrc")) {
- fileName = QLatin1Char(':') + url.path();
+ if (scheme == "qrc"_L1) {
+ fileName = u':' + url.path();
} else {
#if defined(Q_OS_ANDROID)
- if (scheme == QLatin1String("assets"))
- fileName = QLatin1String("assets:") + url.path();
+ if (scheme == "assets"_L1)
+ fileName = "assets:"_L1 + url.path();
else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
@@ -116,7 +85,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
if (req.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
auto realFile = new QNetworkFile(fileName);
- connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setHeader,
+ connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setWellKnownHeader,
Qt::QueuedConnection);
connect(realFile, &QNetworkFile::error, this, &QNetworkReplyFileImpl::setError,
Qt::QueuedConnection);
@@ -134,7 +103,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
if (fi.isDir()) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
setError(QNetworkReply::ContentOperationNotPermittedError, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
return;
@@ -149,18 +118,22 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
if (fi.exists()) {
setError(QNetworkReply::ContentAccessDenied, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
} else {
setError(QNetworkReply::ContentNotFoundError, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
}
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
return;
}
- setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
- setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
+ auto h = headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::LastModified,
+ QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(fi.size()));
+ setHeaders(std::move(h));
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
@@ -202,9 +175,9 @@ bool QNetworkReplyFileImpl::isSequential () const
qint64 QNetworkReplyFileImpl::size() const
{
- bool ok;
- int size = header(QNetworkRequest::ContentLengthHeader).toInt(&ok);
- return ok ? size : 0;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ return totalSizeOpt.value_or(0);
}
/*!
@@ -222,7 +195,7 @@ qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
return -1;
else {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, QLatin1String("OK"));
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, "OK"_L1);
return ret;
}
}
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index 48d82abd3f..6413903d8f 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKREPLYFILEIMPL_H
-#define QNETWORKREPLYFILEIMPL_H
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREPLYFILEIMPL_P_H
+#define QNETWORKREPLYFILEIMPL_P_H
//
// W A R N I N G
@@ -55,7 +19,9 @@
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
+
#include <QFile>
+#include <QtCore/qpointer.h>
#include <private/qabstractfileengine_p.h>
QT_BEGIN_NAMESPACE
@@ -97,6 +63,8 @@ public:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders)
+// ### move to qnetworkrequest.h
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders,
+ QNetworkRequest__KnownHeaders, Q_NETWORK_EXPORT)
-#endif // QNETWORKREPLYFILEIMPL_H
+#endif // QNETWORKREPLYFILEIMPL_P_H
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index ba9d0a76d5..89458825e9 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKACCESSHTTPBACKEND_DEBUG
@@ -57,24 +21,29 @@
#include "QtCore/qcoreapplication.h"
#include <QtCore/private/qthread_p.h>
+#include <QtCore/private/qtools_p.h>
#include "qnetworkcookiejar.h"
#include "qnetconmonitor_p.h"
+#include "qnetworkreplyimpl_p.h"
+
#include <string.h> // for strchr
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
+using namespace std::chrono_literals;
+
class QNetworkProxy;
-static inline bool isSeparator(char c)
-{
- static const char separators[] = "()<>@,;:\\\"/[]?={}";
- return isLWS(c) || strchr(separators, c) != nullptr;
-}
+static inline QByteArray rangeName() { return "Range"_ba; }
+static inline QByteArray cacheControlName() { return "Cache-Control"_ba; }
+static constexpr QByteArrayView bytesEqualPrefix() noexcept { return "bytes="; }
// ### merge with nextField in cookiejar.cpp
-static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
+static QHash<QByteArray, QByteArray> parseHttpOptionHeader(QByteArrayView header)
{
// The HTTP header is of the form:
// header = #1(directives)
@@ -86,7 +55,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
while (true) {
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
return result; // end of parsing
// pos points to a non-whitespace
@@ -100,36 +69,36 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
// of the header, whichever comes first
int end = comma;
if (end == -1)
- end = header.length();
+ end = header.size();
if (equal != -1 && end > equal)
end = equal; // equal sign comes before comma/end
- QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
+ const auto key = header.sliced(pos, end - pos).trimmed();
pos = end + 1;
if (uint(equal) < uint(comma)) {
// case: token "=" (token | quoted-string)
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
// huh? Broken header
return result;
QByteArray value;
- value.reserve(header.length() - pos);
+ value.reserve(header.size() - pos);
if (header.at(pos) == '"') {
// case: quoted-string
// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
// qdtext = <any TEXT except <">>
// quoted-pair = "\" CHAR
++pos;
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (c == '"') {
// end of quoted text
break;
} else if (c == '\\') {
++pos;
- if (pos >= header.length())
+ if (pos >= header.size())
// broken header
return result;
c = header.at(pos);
@@ -139,8 +108,13 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
++pos;
}
} else {
+ const auto isSeparator = [](char c) {
+ static const char separators[] = "()<>@,;:\\\"/[]?={}";
+ return isLWS(c) || strchr(separators, c) != nullptr;
+ };
+
// case: token
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (isSeparator(c))
break;
@@ -149,7 +123,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
}
}
- result.insert(key, value);
+ result.insert(key.toByteArray().toLower(), value);
// find the comma now:
comma = header.indexOf(',', pos);
@@ -159,26 +133,11 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
} else {
// case: token
// key is already set
- result.insert(key, QByteArray());
+ result.insert(key.toByteArray().toLower(), QByteArray());
}
}
}
-#if QT_CONFIG(bearermanagement)
-static bool isSessionNeeded(const QUrl &url)
-{
- if (QNetworkStatusMonitor::isEnabled()) {
- // In case QNetworkStatus/QNetConManager are in business,
- // no session, no bearer manager are involved.
- return false;
- }
- // Connections to the local machine does not require a session
- QString host = url.host().toLower();
- return !QHostAddress(host).isLoopback() && host != QLatin1String("localhost")
- && host != QSysInfo::machineHostName().toLower();
-}
-#endif // bearer management
-
QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manager,
const QNetworkRequest& request,
QNetworkAccessManager::Operation& operation,
@@ -195,10 +154,13 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
d->outgoingData = outgoingData;
d->url = request.url();
#ifndef QT_NO_SSL
- if (request.url().scheme() == QLatin1String("https"))
+ if (request.url().scheme() == "https"_L1)
d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
#endif
+ QObjectPrivate::connect(this, &QNetworkReplyHttpImpl::redirectAllowed, d,
+ &QNetworkReplyHttpImplPrivate::followRedirect, Qt::QueuedConnection);
+
// FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
QIODevice::open(QIODevice::ReadOnly);
@@ -212,7 +174,7 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
if (d->synchronous && outgoingData) {
// The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
// Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
- d->outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+ d->outgoingDataBuffer = std::make_shared<QRingBuffer>();
qint64 previousDataSize = 0;
do {
previousDataSize = d->outgoingDataBuffer->size();
@@ -240,7 +202,10 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
if (bufferingDisallowed) {
// if a valid content-length header for the request was supplied, we can disable buffering
// if not, we will buffer anyway
- if (request.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ request.headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ if (sizeOpt) {
QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
// FIXME make direct call?
} else {
@@ -296,10 +261,6 @@ void QNetworkReplyHttpImpl::abort()
// call finished which will emit signals
// FIXME shouldn't this be emitted Queued?
d->error(OperationCanceledError, tr("Operation canceled"));
-
- // If state is WaitingForSession, calling finished has no effect
- if (d->state == QNetworkReplyPrivate::WaitingForSession)
- d->state = QNetworkReplyPrivate::Working;
d->finished();
}
@@ -322,6 +283,13 @@ qint64 QNetworkReplyHttpImpl::bytesAvailable() const
return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
}
+ if (d->decompressHelper.isValid()) {
+ if (d->decompressHelper.isCountingBytes())
+ return QNetworkReply::bytesAvailable() + d->decompressHelper.uncompressedSize();
+ if (d->decompressHelper.hasData())
+ return QNetworkReply::bytesAvailable() + 1;
+ }
+
// normal buffer
return QNetworkReply::bytesAvailable();
}
@@ -362,6 +330,32 @@ qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
}
+ if (d->decompressHelper.isValid() && (d->decompressHelper.hasData() || !isFinished())) {
+ if (maxlen == 0 || !d->decompressHelper.hasData())
+ return 0;
+ const qint64 bytesRead = d->decompressHelper.read(data, maxlen);
+ if (!d->decompressHelper.isValid()) {
+ d->error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(d->decompressHelper.errorString()));
+ d->decompressHelper.clear();
+ return -1;
+ }
+ if (d->cacheSaveDevice) {
+ // Need to write to the cache now that we have the data
+ d->cacheSaveDevice->write(data, bytesRead);
+ // ... and if we've read everything then the cache can be closed.
+ if (isFinished() && !d->decompressHelper.hasData())
+ d->completeCacheSave();
+ }
+ // In case of buffer size restriction we need to emit that it has been emptied
+ qint64 wasBuffered = d->bytesBuffered;
+ d->bytesBuffered = 0;
+ if (readBufferSize())
+ emit readBufferFreed(wasBuffered);
+ return bytesRead;
+ }
+
// normal buffer
if (d->state == d->Finished || d->state == d->Aborted)
return -1;
@@ -458,15 +452,14 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
, cacheSaveDevice(nullptr)
, cacheEnabled(false)
, resumeOffset(0)
- , preMigrationDownloaded(-1)
, bytesDownloaded(0)
, bytesBuffered(0)
, transferTimeout(nullptr)
, downloadBufferReadPosition(0)
, downloadBufferCurrentSize(0)
, downloadZerocopyBuffer(nullptr)
- , pendingDownloadDataEmissions(QSharedPointer<QAtomicInt>::create())
- , pendingDownloadProgressEmissions(QSharedPointer<QAtomicInt>::create())
+ , pendingDownloadDataEmissions(std::make_shared<QAtomicInt>())
+ , pendingDownloadProgressEmissions(std::make_shared<QAtomicInt>())
#ifndef QT_NO_SSL
, pendingIgnoreAllSslErrors(false)
#endif
@@ -488,19 +481,22 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
{
QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
(QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
+
+ auto requestHeaders = request.headers();
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
// If the request does not already specify preferred cache-control
// force reload from the network and tell any caching proxy servers to reload too
- if (!request.rawHeaderList().contains("Cache-Control")) {
- httpRequest.setHeaderField("Cache-Control", "no-cache");
- httpRequest.setHeaderField("Pragma", "no-cache");
+ if (!requestHeaders.contains(QHttpHeaders::WellKnownHeader::CacheControl)) {
+ const auto noCache = "no-cache"_ba;
+ httpRequest.setHeaderField(cacheControlName(), noCache);
+ httpRequest.setHeaderField("Pragma"_ba, noCache);
}
return false;
}
// The disk cache API does not currently support partial content retrieval.
// That is why we don't use the disk cache for any such requests.
- if (request.hasRawHeader("Range"))
+ if (requestHeaders.contains(QHttpHeaders::WellKnownHeader::Range))
return false;
QAbstractNetworkCache *nc = managerPrivate->networkCache;
@@ -514,24 +510,30 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (!metaData.saveToDisk())
return false;
- QNetworkHeadersPrivate cacheHeaders;
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ QHttpHeaders cacheHeaders = metaData.headers();
- it = cacheHeaders.findRawHeader("etag");
- if (it != cacheHeaders.rawHeaders.constEnd())
- httpRequest.setHeaderField("If-None-Match", it->second);
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ cacheHeaders.value(QHttpHeaders::WellKnownHeader::ContentLength));
+ if (sizeOpt) {
+ std::unique_ptr<QIODevice> data(nc->data(httpRequest.url()));
+ if (!data || data->size() < sizeOpt.value())
+ return false; // The data is smaller than the content-length specified
+ }
+
+ auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::ETag);
+ if (!value.empty())
+ httpRequest.setHeaderField("If-None-Match"_ba, value.toByteArray());
QDateTime lastModified = metaData.lastModified();
if (lastModified.isValid())
- httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
+ httpRequest.setHeaderField("If-Modified-Since"_ba, QNetworkHeadersPrivate::toHttpDate(lastModified));
- it = cacheHeaders.findRawHeader("Cache-Control");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::CacheControl);
+ if (!value.empty()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
+ if (cacheControl.contains("must-revalidate"_ba))
return false;
- if (cacheControl.contains("no-cache"))
+ if (cacheControl.contains("no-cache"_ba))
return false;
}
@@ -555,16 +557,15 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
* now
* is the current (local) time
*/
- qint64 age_value = 0;
- it = cacheHeaders.findRawHeader("age");
- if (it != cacheHeaders.rawHeaders.constEnd())
- age_value = it->second.toLongLong();
+ const auto ageOpt = QNetworkHeadersPrivate::toInt(
+ cacheHeaders.value(QHttpHeaders::WellKnownHeader::Age));
+ const qint64 age_value = ageOpt.value_or(0);
QDateTime dateHeader;
qint64 date_value = 0;
- it = cacheHeaders.findRawHeader("date");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::Date);
+ if (!value.empty()) {
+ dateHeader = QNetworkHeadersPrivate::fromHttpDate(value);
date_value = dateHeader.toSecsSinceEpoch();
}
@@ -586,10 +587,11 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (lastModified.isValid() && dateHeader.isValid()) {
qint64 diff = lastModified.secsTo(dateHeader);
freshness_lifetime = diff / 10;
- if (httpRequest.headerField("Warning").isEmpty()) {
+ const auto warningHeader = "Warning"_ba;
+ if (httpRequest.headerField(warningHeader).isEmpty()) {
QDateTime dt = currentDateTime.addSecs(current_age);
if (currentDateTime.daysTo(dt) > 1)
- httpRequest.setHeaderField("Warning", "113");
+ httpRequest.setHeaderField(warningHeader, "113"_ba);
}
}
@@ -645,13 +647,11 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setRedirectCount(newHttpRequest.maximumRedirectsAllowed());
QString scheme = url.scheme();
- bool ssl = (scheme == QLatin1String("https")
- || scheme == QLatin1String("preconnect-https"));
+ bool ssl = (scheme == "https"_L1 || scheme == "preconnect-https"_L1);
q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
httpRequest.setSsl(ssl);
- bool preConnect = (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https"));
+ bool preConnect = (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1);
httpRequest.setPreConnect(preConnect);
#ifndef QT_NO_NETWORKPROXY
@@ -690,22 +690,26 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
}
#endif
- auto redirectPolicy = QNetworkRequest::ManualRedirectPolicy;
+ auto redirectPolicy = QNetworkRequest::NoLessSafeRedirectPolicy;
const QVariant value = newHttpRequest.attribute(QNetworkRequest::RedirectPolicyAttribute);
if (value.isValid())
- redirectPolicy = value.value<QNetworkRequest::RedirectPolicy>();
- else if (newHttpRequest.attribute(QNetworkRequest::FollowRedirectsAttribute).toBool())
- redirectPolicy = QNetworkRequest::NoLessSafeRedirectPolicy;
+ redirectPolicy = qvariant_cast<QNetworkRequest::RedirectPolicy>(value);
httpRequest.setRedirectPolicy(redirectPolicy);
httpRequest.setPriority(convert(newHttpRequest.priority()));
+ loadingFromCache = false;
switch (operation) {
case QNetworkAccessManager::GetOperation:
httpRequest.setOperation(QHttpNetworkRequest::Get);
- if (loadFromCacheIfAllowed(httpRequest))
+ // If the request has a body, createUploadByteDevice() and don't use caching
+ if (outgoingData) {
+ invalidateCache();
+ createUploadByteDevice();
+ } else if (loadFromCacheIfAllowed(httpRequest)) {
return; // no need to send the request! :)
+ }
break;
case QNetworkAccessManager::HeadOperation:
@@ -743,16 +747,16 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
break; // can't happen
}
- QList<QByteArray> headers = newHttpRequest.rawHeaderList();
+ QHttpHeaders newRequestHeaders = newHttpRequest.headers();
if (resumeOffset != 0) {
- const int rangeIndex = headers.indexOf("Range");
- if (rangeIndex != -1) {
+ if (newRequestHeaders.contains(QHttpHeaders::WellKnownHeader::Range)) {
// Need to adjust resume offset for user specified range
- headers.removeAt(rangeIndex);
-
// We've already verified that requestRange starts with "bytes=", see canResume.
- QByteArray requestRange = newHttpRequest.rawHeader("Range").mid(6);
+ const auto rangeHeader = newRequestHeaders.value(QHttpHeaders::WellKnownHeader::Range);
+ const auto requestRange = QByteArrayView(rangeHeader).mid(bytesEqualPrefix().size());
+
+ newRequestHeaders.removeAll(QHttpHeaders::WellKnownHeader::Range);
int index = requestRange.indexOf('-');
@@ -760,26 +764,34 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
// In case an end offset is not given it is skipped from the request range
- requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
+ QByteArray newRange = bytesEqualPrefix() + QByteArray::number(resumeOffset + requestStartOffset) +
'-' + (requestEndOffset ? QByteArray::number(requestEndOffset) : QByteArray());
- httpRequest.setHeaderField("Range", requestRange);
+ httpRequest.setHeaderField(rangeName(), newRange);
} else {
- httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
+ httpRequest.setHeaderField(rangeName(), bytesEqualPrefix() + QByteArray::number(resumeOffset) + '-');
}
}
- for (const QByteArray &header : qAsConst(headers))
- httpRequest.setHeaderField(header, newHttpRequest.rawHeader(header));
+ for (int i = 0; i < newRequestHeaders.size(); i++) {
+ const auto name = newRequestHeaders.nameAt(i);
+ const auto value = newRequestHeaders.valueAt(i);
+ httpRequest.setHeaderField(QByteArray(name.data(), name.size()), value.toByteArray());
+ }
if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool())
httpRequest.setPipeliningAllowed(true);
- if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool())
- httpRequest.setSPDYAllowed(true);
-
- if (request.attribute(QNetworkRequest::Http2AllowedAttribute).toBool())
- httpRequest.setHTTP2Allowed(true);
+ if (auto allowed = request.attribute(QNetworkRequest::Http2AllowedAttribute);
+ allowed.isValid() && allowed.canConvert<bool>()) {
+ httpRequest.setHTTP2Allowed(allowed.value<bool>());
+ }
+ auto h2cAttribute = request.attribute(QNetworkRequest::Http2CleartextAllowedAttribute);
+ // ### Qt7: Stop checking the environment variable
+ if (h2cAttribute.toBool()
+ || (!h2cAttribute.isValid() && qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED"))) {
+ httpRequest.setH2cAllowed(true);
+ }
if (request.attribute(QNetworkRequest::Http2DirectAttribute).toBool()) {
// Intentionally mutually exclusive - cannot be both direct and 'allowed'
@@ -797,18 +809,35 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName());
+ if (scheme.startsWith(("unix"_L1))) {
+ if (QVariant path = newHttpRequest.attribute(QNetworkRequest::FullLocalServerNameAttribute);
+ path.isValid() && path.canConvert<QString>()) {
+ httpRequest.setFullLocalServerName(path.toString());
+ }
+ }
+
// Create the HTTP thread delegate
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
// Propagate Http/2 settings:
delegate->http2Parameters = request.http2Configuration();
-#ifndef QT_NO_BEARERMANAGEMENT
- if (!QNetworkStatusMonitor::isEnabled())
- delegate->networkSession = managerPrivate->getNetworkSession();
-#endif
+ delegate->http1Parameters = request.http1Configuration();
+
+ if (request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).isValid())
+ delegate->connectionCacheExpiryTimeoutSeconds = request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).toInt();
// For the synchronous HTTP, this is the normal way the delegate gets deleted
// For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
- QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+ QMetaObject::Connection threadFinishedConnection =
+ QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+
+ // QTBUG-88063: When 'delegate' is deleted the connection will be added to 'thread''s orphaned
+ // connections list. This orphaned list will be cleaned up next time 'thread' emits a signal,
+ // unfortunately that's the finished signal. It leads to a soft-leak so we do this to disconnect
+ // it on deletion so that it cleans up the orphan immediately.
+ QObject::connect(delegate, &QObject::destroyed, delegate, [threadFinishedConnection]() {
+ if (bool(threadFinishedConnection))
+ QObject::disconnect(threadFinishedConnection);
+ });
// Set the properties it needs
delegate->httpRequest = httpRequest;
@@ -853,14 +882,12 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QObject::connect(delegate, SIGNAL(downloadFinished()),
q, SLOT(replyFinished()),
Qt::QueuedConnection);
- QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool,
- QSharedPointer<char>, qint64, qint64,
- bool)),
- q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool,
- QSharedPointer<char>, qint64, qint64, bool)),
- Qt::QueuedConnection);
+ QObject::connect(delegate, &QHttpThreadDelegate::socketStartedConnecting,
+ q, &QNetworkReply::socketStartedConnecting, Qt::QueuedConnection);
+ QObject::connect(delegate, &QHttpThreadDelegate::requestSent,
+ q, &QNetworkReply::requestSent, Qt::QueuedConnection);
+ connect(delegate, &QHttpThreadDelegate::downloadMetaData, this,
+ &QNetworkReplyHttpImplPrivate::replyDownloadMetaData, Qt::QueuedConnection);
QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
Qt::QueuedConnection);
@@ -871,9 +898,6 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
q, SLOT(onRedirected(QUrl,int,int)),
Qt::QueuedConnection);
- QObject::connect(q, SIGNAL(redirectAllowed()), q, SLOT(followRedirect()),
- Qt::QueuedConnection);
-
#ifndef QT_NO_SSL
QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
@@ -913,14 +937,14 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
// If the device in the user thread claims it has more data, keep the flow to HTTP thread going
- QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
+ QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
q, SLOT(uploadByteDeviceReadyReadSlot()),
Qt::QueuedConnection);
// From user thread to http thread:
QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)),
forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection);
- QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
+ QObject::connect(uploadByteDevice.get(), SIGNAL(readyRead()),
forwardUploadDevice, SIGNAL(readyRead()),
Qt::QueuedConnection);
@@ -943,7 +967,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
// use the uploadByteDevice provided to us by the QNetworkReplyImpl.
// The code that is in start() makes sure it is safe to use from a thread
// since it only wraps a QRingBuffer
- delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
+ delegate->httpRequest.setUploadByteDevice(uploadByteDevice.get());
}
}
@@ -960,30 +984,20 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (synchronous) {
emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
- if (delegate->incomingErrorCode != QNetworkReply::NoError) {
- replyDownloadMetaData
- (delegate->incomingHeaders,
- delegate->incomingStatusCode,
- delegate->incomingReasonPhrase,
- delegate->isPipeliningUsed,
- QSharedPointer<char>(),
- delegate->incomingContentLength,
- delegate->removedContentLength,
- delegate->isSpdyUsed);
- replyDownloadData(delegate->synchronousDownloadData);
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength,
+ delegate->removedContentLength,
+ delegate->isHttp2Used,
+ delegate->isCompressed);
+ replyDownloadData(delegate->synchronousDownloadData);
+
+ if (delegate->incomingErrorCode != QNetworkReply::NoError)
httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
- } else {
- replyDownloadMetaData
- (delegate->incomingHeaders,
- delegate->incomingStatusCode,
- delegate->incomingReasonPhrase,
- delegate->isPipeliningUsed,
- QSharedPointer<char>(),
- delegate->incomingContentLength,
- delegate->removedContentLength,
- delegate->isSpdyUsed);
- replyDownloadData(delegate->synchronousDownloadData);
- }
thread->quit();
thread->wait(QDeadlineTimer(5000));
@@ -1054,23 +1068,79 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (!q->isOpen())
return;
+ // cache this, we need it later and it's invalidated when dealing with compressed data
+ auto dataSize = d.size();
+
if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice)
initCacheSaveDevice();
+ if (decompressHelper.isValid()) {
+ qint64 uncompressedBefore = -1;
+ if (decompressHelper.isCountingBytes())
+ uncompressedBefore = decompressHelper.uncompressedSize();
+
+ decompressHelper.feed(std::move(d));
+
+ if (!decompressHelper.isValid()) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(decompressHelper.errorString()));
+ decompressHelper.clear();
+ return;
+ }
+
+ if (!isHttpRedirectResponse()) {
+ if (decompressHelper.isCountingBytes())
+ bytesDownloaded += (decompressHelper.uncompressedSize() - uncompressedBefore);
+ setupTransferTimeout();
+ }
+
+ if (synchronous) {
+ d = QByteArray();
+ const qsizetype increments = 16 * 1024;
+ qint64 bytesRead = 0;
+ while (decompressHelper.hasData()) {
+ quint64 nextSize = quint64(d.size()) + quint64(increments);
+ if (nextSize > quint64(std::numeric_limits<QByteArray::size_type>::max())) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp",
+ "Data downloaded is too large to store"));
+ decompressHelper.clear();
+ return;
+ }
+ d.resize(nextSize);
+ bytesRead += decompressHelper.read(d.data() + bytesRead, increments);
+ if (!decompressHelper.isValid()) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(decompressHelper.errorString()));
+ decompressHelper.clear();
+ return;
+ }
+ }
+ d.resize(bytesRead);
+ // we're synchronous so we're not calling this function again; reset the decompressHelper
+ decompressHelper.clear();
+ }
+ }
+
// This is going to look a little strange. When downloading data while a
// HTTP redirect is happening (and enabled), we write the redirect
// response to the cache. However, we do not append it to our internal
// buffer as that will contain the response data only for the final
// response
- if (cacheSaveDevice)
+ // Note: For compressed data this is done in readData()
+ if (cacheSaveDevice && !decompressHelper.isValid()) {
cacheSaveDevice->write(d);
+ }
- if (!isHttpRedirectResponse()) {
+ // if decompressHelper is valid then we have compressed data, and this is handled above
+ if (!decompressHelper.isValid() && !isHttpRedirectResponse()) {
buffer.append(d);
- bytesDownloaded += d.size();
+ bytesDownloaded += dataSize;
setupTransferTimeout();
}
- bytesBuffered += d.size();
+ bytesBuffered += dataSize;
int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1;
if (pendingSignals > 0) {
@@ -1084,19 +1154,26 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (isHttpRedirectResponse())
return;
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- if (preMigrationDownloaded != Q_INT64_C(-1))
- totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ // This can occur when downloading compressed data as some of the data may be the content
+ // encoding's header. Don't emit anything for this.
+ if (lastReadyReadEmittedSize == bytesDownloaded) {
+ if (readBufferMaxSize)
+ emit q->readBufferFreed(dataSize);
+ return;
+ }
+ lastReadyReadEmittedSize = bytesDownloaded;
+
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
emit q->readyRead();
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
- if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
+ if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval
+ && (!decompressHelper.isValid() || decompressHelper.isCountingBytes())) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
-
}
void QNetworkReplyHttpImplPrivate::replyFinished()
@@ -1158,7 +1235,8 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
url = redirectUrl;
- if (managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
+ const bool wasLocalSocket = schemeBefore.startsWith("unix"_L1);
+ if (!wasLocalSocket && managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
// RFC6797, 8.3:
// The UA MUST replace the URI scheme with "https" [RFC2818],
// and if the URI contains an explicit port component of "80",
@@ -1167,31 +1245,61 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
// equal to "80", the port component value MUST be preserved;
// otherwise, if the URI does not contain an explicit port
// component, the UA MUST NOT add one.
- url.setScheme(QLatin1String("https"));
+ url.setScheme("https"_L1);
if (url.port() == 80)
url.setPort(443);
}
- const bool isLessSafe = schemeBefore == QLatin1String("https")
- && url.scheme() == QLatin1String("http");
- if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy
- && isLessSafe) {
+ // Just to be on the safe side for local sockets, any changes to the scheme
+ // are considered less safe
+ const bool changingLocalScheme = wasLocalSocket && url.scheme() != schemeBefore;
+ const bool isLessSafe = changingLocalScheme
+ || (schemeBefore == "https"_L1 && url.scheme() == "http"_L1);
+ if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy && isLessSafe) {
error(QNetworkReply::InsecureRedirectError,
QCoreApplication::translate("QHttp", "Insecure redirect"));
return;
}
+ // If the original operation was a GET with a body and the status code is either
+ // 307 or 308 then keep the message body
+ const bool getOperationKeepsBody = (operation == QNetworkAccessManager::GetOperation)
+ && (httpStatus == 307 || httpStatus == 308);
+
redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
operation = getRedirectOperation(operation, httpStatus);
+ // Clear stale headers, the relevant ones get set again later
+ httpRequest.clearHeaders();
+ auto newHeaders = redirectRequest.headers();
+ if ((operation == QNetworkAccessManager::GetOperation
+ || operation == QNetworkAccessManager::HeadOperation) && !getOperationKeepsBody) {
+ // possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
+ uploadByteDevice.reset();
+ uploadByteDevicePosition = 0;
+ if (outgoingData) {
+ QObject::disconnect(outgoingData, SIGNAL(readyRead()), q,
+ SLOT(_q_bufferOutgoingData()));
+ QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q,
+ SLOT(_q_bufferOutgoingDataFinished()));
+ }
+ outgoingData = nullptr;
+ outgoingDataBuffer.reset();
+ // We need to explicitly unset these headers so they're not reapplied to the httpRequest
+ newHeaders.removeAll(QHttpHeaders::WellKnownHeader::ContentLength);
+ newHeaders.removeAll(QHttpHeaders::WellKnownHeader::ContentType);
+ }
+
if (const QNetworkCookieJar *const cookieJar = manager->cookieJar()) {
auto cookies = cookieJar->cookiesForUrl(url);
if (!cookies.empty()) {
- redirectRequest.setHeader(QNetworkRequest::KnownHeaders::CookieHeader,
- QVariant::fromValue(cookies));
+ auto cookieHeader = QNetworkHeadersPrivate::fromCookieList(cookies);
+ newHeaders.replaceOrAppend(QHttpHeaders::WellKnownHeader::Cookie, cookieHeader);
}
}
+ redirectRequest.setHeaders(std::move(newHeaders));
+
if (httpRequest.redirectPolicy() != QNetworkRequest::UserVerifiedRedirectPolicy)
followRedirect();
@@ -1203,34 +1311,18 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
Q_Q(QNetworkReplyHttpImpl);
Q_ASSERT(managerPrivate);
- rawHeaders.clear();
- cookedHeaders.clear();
+ decompressHelper.clear();
+ clearHeaders();
if (managerPrivate->thread)
managerPrivate->thread->disconnect();
-#if QT_CONFIG(bearermanagement)
- // If the original request didn't need a session (i.e. it was to localhost)
- // then we might not have a session open, to which to redirect, if the
- // new URL is remote. When this happens, we need to open the session now:
- if (isSessionNeeded(url)) {
- if (auto session = managerPrivate->getNetworkSession()) {
- if (session->state() != QNetworkSession::State::Connected || !session->isOpen()) {
- startWaitForSession(session);
- // Need to set 'request' to the redirectRequest so that when QNAM restarts
- // the request after the session starts it will not repeat the previous request.
- request = redirectRequest;
- // Return now, QNAM will start the request when the session has started.
- return;
- }
- }
- }
-#endif // bearer management
-
- QMetaObject::invokeMethod(q, "start", Qt::QueuedConnection,
- Q_ARG(QNetworkRequest, redirectRequest));
+ QMetaObject::invokeMethod(
+ q, [this]() { postRequest(redirectRequest); }, Qt::QueuedConnection);
}
+static constexpr QLatin1StringView locationHeader() noexcept { return "location"_L1; }
+
void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
{
Q_Q(QNetworkReplyHttpImpl);
@@ -1243,20 +1335,20 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
// What do we do about the caching of the HTML note?
// The response to a 303 MUST NOT be cached, while the response to
// all of the others is cacheable if the headers indicate it to be
- QByteArray header = q->rawHeader("location");
+ QByteArrayView header = q->headers().value(locationHeader());
QUrl url = QUrl(QString::fromUtf8(header));
if (!url.isValid())
- url = QUrl(QLatin1String(header));
+ url = QUrl(QLatin1StringView(header));
q->setAttribute(QNetworkRequest::RedirectionTargetAttribute, url);
}
}
-void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm,
+void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
int sc, const QString &rp, bool pu,
QSharedPointer<char> db,
qint64 contentLength,
qint64 removedContentLength,
- bool spdyWasUsed)
+ bool h2Used, bool isCompressed)
{
Q_Q(QNetworkReplyHttpImpl);
Q_UNUSED(contentLength);
@@ -1270,7 +1362,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
// RFC6797, 8.1
// If an HTTP response is received over insecure transport, the UA MUST
// ignore any present STS header field(s).
- if (url.scheme() == QLatin1String("https") && managerPrivate->stsEnabled)
+ if (url.scheme() == "https"_L1 && managerPrivate->stsEnabled)
managerPrivate->stsCache.updateFromHeaders(hm, url);
#endif
// Download buffer
@@ -1282,41 +1374,42 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
}
q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
- const QVariant http2Allowed = request.attribute(QNetworkRequest::Http2AllowedAttribute);
- const QVariant http2Direct = request.attribute(QNetworkRequest::Http2DirectAttribute);
- if ((http2Allowed.isValid() && http2Allowed.toBool())
- || (http2Direct.isValid() && http2Direct.toBool())) {
- q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, spdyWasUsed);
- q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, false);
- } else {
- q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, spdyWasUsed);
- q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, false);
- }
+ q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used);
+ // A user having manually defined which encodings they accept is, for
+ // somwehat unknown (presumed legacy compatibility) reasons treated as
+ // disabling our decompression:
+ const bool autoDecompress = !request.headers().contains(QHttpHeaders::WellKnownHeader::AcceptEncoding);
+ const bool shouldDecompress = isCompressed && autoDecompress;
// reconstruct the HTTP header
- QList<QPair<QByteArray, QByteArray> > headerMap = hm;
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
- end = headerMap.constEnd();
- for (; it != end; ++it) {
- QByteArray value = q->rawHeader(it->first);
+ auto h = q->headers();
+ for (qsizetype i = 0; i < hm.size(); ++i) {
+ const auto key = hm.nameAt(i);
+ const auto originValue = hm.valueAt(i);
// Reset any previous "location" header set in the reply. In case of
// redirects, we don't want to 'append' multiple location header values,
// rather we keep only the latest one
- if (it->first.toLower() == "location")
- value.clear();
-
- if (!value.isEmpty()) {
- // Why are we appending values for headers which are already
- // present?
- if (it->first.compare("set-cookie", Qt::CaseInsensitive) == 0)
- value += '\n';
- else
- value += ", ";
+ if (key.compare(locationHeader(), Qt::CaseInsensitive) == 0)
+ h.removeAll(key);
+
+ if (shouldDecompress && !decompressHelper.isValid() && key == "content-encoding"_L1) {
+ if (!synchronous) // with synchronous all the data is expected to be handled at once
+ decompressHelper.setCountingBytesEnabled(true);
+
+ if (!decompressHelper.setEncoding(originValue)) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Failed to initialize decompression: %1")
+ .arg(decompressHelper.errorString()));
+ return;
+ }
+ decompressHelper.setDecompressedSafetyCheckThreshold(
+ request.decompressedSafetyCheckThreshold());
}
- value += it->second;
- q->setRawHeader(it->first, value);
+
+ h.append(key, originValue);
}
+ q->setHeaders(std::move(h));
q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
@@ -1331,14 +1424,11 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
QAbstractNetworkCache *nc = managerPrivate->networkCache;
if (nc) {
QNetworkCacheMetaData metaData = nc->metaData(httpRequest.url());
- QNetworkHeadersPrivate cacheHeaders;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- it = cacheHeaders.findRawHeader("Cache-Control");
+ auto value = metaData.headers().value(QHttpHeaders::WellKnownHeader::CacheControl);
bool mustReValidate = false;
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ if (!value.empty()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
+ if (cacheControl.contains("must-revalidate"_ba))
mustReValidate = true;
}
if (!mustReValidate && sendCacheContents(metaData))
@@ -1408,7 +1498,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceive
downloadBufferCurrentSize = bytesReceived;
// Only emit readyRead when actual data is there
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
if (bytesDownloaded > 0)
emit q->readyRead();
@@ -1491,6 +1581,9 @@ void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount)
{
+ if (!uploadByteDevice) // uploadByteDevice is no longer available
+ return;
+
if (uploadByteDevicePosition + amount != pos) {
// Sanity check, should not happen.
error(QNetworkReply::UnknownNetworkError, QString());
@@ -1505,6 +1598,9 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
{
Q_Q(QNetworkReplyHttpImpl);
+ if (!uploadByteDevice) // uploadByteDevice is no longer available
+ return;
+
// call readPointer
qint64 currentUploadDataLength = 0;
char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
@@ -1570,16 +1666,21 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
q->setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
- QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders();
- QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
+ QHttpHeaders cachedHeaders = metaData.headers();
+ QHttpHeaders h = headers();
QUrl redirectUrl;
- for ( ; it != end; ++it) {
- if (httpRequest.isFollowRedirects() &&
- !it->first.compare("location", Qt::CaseInsensitive))
- redirectUrl = QUrl::fromEncoded(it->second);
- setRawHeader(it->first, it->second);
+ for (qsizetype i = 0; i < cachedHeaders.size(); ++i) {
+ const auto name = cachedHeaders.nameAt(i);
+ const auto value = cachedHeaders.valueAt(i);
+
+ if (httpRequest.isFollowRedirects()
+ && !name.compare(locationHeader(), Qt::CaseInsensitive)) {
+ redirectUrl = QUrl::fromEncoded(value);
+ }
+
+ h.replaceOrAppend(name, value);
}
+ setHeaders(std::move(h));
if (!isHttpRedirectResponse())
checkForRedirect(status);
@@ -1613,33 +1714,43 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
return true;
}
+static auto caseInsensitiveCompare(QByteArrayView value)
+{
+ return [value](QByteArrayView element)
+ {
+ return value.compare(element, Qt::CaseInsensitive) == 0;
+ };
+}
+
+static bool isHopByHop(QByteArrayView header)
+{
+ constexpr QByteArrayView headers[] = { "connection",
+ "keep-alive",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "te",
+ "trailers",
+ "transfer-encoding",
+ "upgrade"};
+ return std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header));
+}
+
QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
{
Q_Q(const QNetworkReplyHttpImpl);
QNetworkCacheMetaData metaData = oldMetaData;
+ QHttpHeaders cacheHeaders = metaData.headers();
+
+ const auto newHeaders = q->headers();
+ for (qsizetype i = 0; i < newHeaders.size(); ++i) {
+ const auto name = newHeaders.nameAt(i);
+ const auto value = newHeaders.valueAt(i);
- QNetworkHeadersPrivate cacheHeaders;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
-
- const QList<QByteArray> newHeaders = q->rawHeaderList();
- for (QByteArray header : newHeaders) {
- QByteArray originalHeader = header;
- header = header.toLower();
- bool hop_by_hop =
- (header == "connection"
- || header == "keep-alive"
- || header == "proxy-authenticate"
- || header == "proxy-authorization"
- || header == "te"
- || header == "trailers"
- || header == "transfer-encoding"
- || header == "upgrade");
- if (hop_by_hop)
+ if (isHopByHop(name))
continue;
- if (header == "set-cookie")
+ if (name.compare("set-cookie", Qt::CaseInsensitive) == 0)
continue;
// for 4.6.0, we were planning to not store the date header in the
@@ -1652,51 +1763,47 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
//continue;
// Don't store Warning 1xx headers
- if (header == "warning") {
- QByteArray v = q->rawHeader(header);
- if (v.length() == 3
- && v[0] == '1'
- && v[1] >= '0' && v[1] <= '9'
- && v[2] >= '0' && v[2] <= '9')
+ if (name.compare("warning", Qt::CaseInsensitive) == 0) {
+ if (value.size() == 3
+ && value[0] == '1'
+ && isAsciiDigit(value[1])
+ && isAsciiDigit(value[2]))
continue;
}
- it = cacheHeaders.findRawHeader(header);
- if (it != cacheHeaders.rawHeaders.constEnd()) {
+ if (cacheHeaders.contains(name)) {
// Match the behavior of Firefox and assume Cache-Control: "no-transform"
- if (header == "content-encoding"
- || header == "content-range"
- || header == "content-type")
+ constexpr QByteArrayView headers[]=
+ {"content-encoding", "content-range", "content-type"};
+ if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(name)))
continue;
}
// IIS has been known to send "Content-Length: 0" on 304 responses, so
// ignore this too
- if (header == "content-length" && statusCode == 304)
+ if (statusCode == 304 && name.compare("content-length", Qt::CaseInsensitive) == 0)
continue;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
- QByteArray n = q->rawHeader(header);
- QByteArray o;
- if (it != cacheHeaders.rawHeaders.constEnd())
- o = (*it).second;
- if (n != o && header != "date") {
- qDebug() << "replacing" << header;
+ QByteArrayView n = newHeaders.value(name);
+ QByteArrayView o = cacheHeaders.value(name);
+ if (n != o && name.compare("date", Qt::CaseInsensitive) != 0) {
+ qDebug() << "replacing" << name;
qDebug() << "new" << n;
qDebug() << "old" << o;
}
#endif
- cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
+ cacheHeaders.replaceOrAppend(name, value);
}
- metaData.setRawHeaders(cacheHeaders.rawHeaders);
+ metaData.setHeaders(cacheHeaders);
bool checkExpired = true;
QHash<QByteArray, QByteArray> cacheControl;
- it = cacheHeaders.findRawHeader("Cache-Control");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- cacheControl = parseHttpOptionHeader(it->second);
- QByteArray maxAge = cacheControl.value("max-age");
+ auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::CacheControl);
+ if (!value.empty()) {
+ cacheControl = parseHttpOptionHeader(value);
+ QByteArray maxAge = cacheControl.value("max-age"_ba);
if (!maxAge.isEmpty()) {
checkExpired = false;
QDateTime dt = QDateTime::currentDateTimeUtc();
@@ -1705,16 +1812,18 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
}
}
if (checkExpired) {
- it = cacheHeaders.findRawHeader("expires");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ if (const auto value = cacheHeaders.value(
+ QHttpHeaders::WellKnownHeader::Expires); !value.isEmpty()) {
+ QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(value);
metaData.setExpirationDate(expiredDateTime);
}
}
- it = cacheHeaders.findRawHeader("last-modified");
- if (it != cacheHeaders.rawHeaders.constEnd())
- metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
+ if (const auto value = cacheHeaders.value(
+ QHttpHeaders::WellKnownHeader::LastModified); !value.isEmpty()) {
+ metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(value));
+ }
+
bool canDiskCache;
// only cache GET replies by default, all other replies (POST, PUT, DELETE)
@@ -1723,7 +1832,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = true;
// HTTP/1.1. Check the Cache-Control header
- if (cacheControl.contains("no-store"))
+ if (cacheControl.contains("no-store"_ba))
canDiskCache = false;
// responses to POST might be cacheable
@@ -1732,7 +1841,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = false;
// some pages contain "expires:" and "cache-control: no-cache" field,
// so we only might cache POST requests if we get "cache-control: max-age ..."
- if (cacheControl.contains("max-age"))
+ if (cacheControl.contains("max-age"_ba))
canDiskCache = true;
// responses to PUT and DELETE are not cacheable
@@ -1762,15 +1871,17 @@ bool QNetworkReplyHttpImplPrivate::canResume() const
if (operation != QNetworkAccessManager::GetOperation)
return false;
+ const auto h = q->headers();
+
// Can only resume if server/resource supports Range header.
- QByteArray acceptRangesheaderName("Accept-Ranges");
- if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
+ const auto acceptRanges = h.value(QHttpHeaders::WellKnownHeader::AcceptRanges);
+ if (acceptRanges.empty() || acceptRanges == "none")
return false;
// We only support resuming for byte ranges.
- if (request.hasRawHeader("Range")) {
- QByteArray range = request.rawHeader("Range");
- if (!range.startsWith("bytes="))
+ const auto range = h.value(QHttpHeaders::WellKnownHeader::Range);
+ if (!range.empty()) {
+ if (!range.startsWith(bytesEqualPrefix()))
return false;
}
@@ -1787,117 +1898,16 @@ void QNetworkReplyHttpImplPrivate::setResumeOffset(quint64 offset)
resumeOffset = offset;
}
-/*!
- Starts the backend. Returns \c true if the backend is started. Returns \c false if the backend
- could not be started due to an unopened or roaming session. The caller should recall this
- function once the session has been opened or the roaming process has finished.
-*/
-bool QNetworkReplyHttpImplPrivate::start(const QNetworkRequest &newHttpRequest)
-{
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession(managerPrivate->getNetworkSession());
- if (!networkSession || QNetworkStatusMonitor::isEnabled()) {
-#endif
- postRequest(newHttpRequest);
- return true;
-#ifndef QT_NO_BEARERMANAGEMENT
- }
-
- // This is not ideal.
- if (!isSessionNeeded(url)) {
- // Don't need to check for an open session if we don't need one.
- postRequest(newHttpRequest);
- return true;
- }
-
- if (networkSession->isOpen() &&
- networkSession->state() == QNetworkSession::Connected) {
- Q_Q(QNetworkReplyHttpImpl);
- QObject::connect(networkSession.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
- q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
- postRequest(newHttpRequest);
- return true;
- } else if (synchronous) {
- // Command line applications using the synchronous path such as xmlpatterns may need an extra push.
- networkSession->open();
- if (networkSession->waitForOpened()) {
- postRequest(newHttpRequest);
- return true;
- }
- }
- return false;
-#endif
-}
-
-#if QT_CONFIG(bearermanagement)
-bool QNetworkReplyHttpImplPrivate::startWaitForSession(QSharedPointer<QNetworkSession> &session)
-{
- Q_Q(QNetworkReplyHttpImpl);
- state = WaitingForSession;
-
- if (session) {
- QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
- q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);
-
- if (!session->isOpen()) {
- QVariant isBackground = request.attribute(QNetworkRequest::BackgroundRequestAttribute,
- QVariant::fromValue(false));
- session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
- session->open();
- }
- return true;
- }
- const Qt::ConnectionType connection = synchronous ? Qt::DirectConnection : Qt::QueuedConnection;
- qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
- QMetaObject::invokeMethod(q, "_q_error", connection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::NetworkSessionFailedError),
- Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Network session error.")));
- QMetaObject::invokeMethod(q, "_q_finished", connection);
- return false;
-}
-#endif // QT_CONFIG(bearermanagement)
-
void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
- Q_Q(QNetworkReplyHttpImpl);
- if (state == Working) // ensure this function is only being called once
+ // Ensure this function is only being called once, and not at all if we were
+ // cancelled
+ if (state >= Working)
return;
state = Working;
-#ifndef QT_NO_BEARERMANAGEMENT
- // Do not start background requests if they are not allowed by session policy
- QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
- QVariant isBackground = request.attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
- if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
- QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::BackgroundRequestNotAllowedError),
- Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Background request not allowed.")));
- QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
- return;
- }
-
- if (!start(request)) {
- // backend failed to start because the session state is not Connected.
- // QNetworkAccessManager will call reply->backend->start() again for us when the session
- // state changes.
- if (!startWaitForSession(session))
- return;
- } else if (session && !QNetworkStatusMonitor::isEnabled()) {
- QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)),
- q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)),
- Qt::QueuedConnection);
- }
-#else
- if (!start(request)) {
- qWarning("Backend start failed");
- QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::UnknownNetworkError),
- Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "backend start error.")));
- QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
- return;
- }
-#endif // QT_NO_BEARERMANAGEMENT
+ postRequest(request);
setupTransferTimeout();
if (synchronous) {
@@ -1919,10 +1929,10 @@ void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
// Needs to be done where sendCacheContents() (?) of HTTP is emitting
// metaDataChanged ?
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
-
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
if (!(isHttpRedirectResponse())) {
@@ -1931,8 +1941,7 @@ void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
}
@@ -1999,7 +2008,7 @@ void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData()
if (!outgoingDataBuffer) {
// first call, create our buffer
- outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+ outgoingDataBuffer = std::make_shared<QRingBuffer>();
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
@@ -2052,89 +2061,15 @@ void QNetworkReplyHttpImplPrivate::setupTransferTimeout()
Qt::QueuedConnection);
}
transferTimeout->stop();
- if (request.transferTimeout()) {
+ if (request.transferTimeoutAsDuration() > 0ms) {
transferTimeout->setSingleShot(true);
- transferTimeout->setInterval(request.transferTimeout());
+ transferTimeout->setInterval(request.transferTimeoutAsDuration());
QMetaObject::invokeMethod(transferTimeout, "start",
Qt::QueuedConnection);
}
}
-#ifndef QT_NO_BEARERMANAGEMENT
-void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected()
-{
- Q_Q(QNetworkReplyHttpImpl);
- Q_ASSERT(managerPrivate);
-
- QSharedPointer<QNetworkSession> session = managerPrivate->getNetworkSession();
- if (!session)
- return;
-
- if (session->state() != QNetworkSession::Connected)
- return;
-
- switch (state) {
- case QNetworkReplyPrivate::Buffering:
- case QNetworkReplyPrivate::Working:
- case QNetworkReplyPrivate::Reconnecting:
- // Migrate existing downloads to new network connection.
- migrateBackend();
- break;
- case QNetworkReplyPrivate::WaitingForSession:
- // Start waiting requests.
- QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
- break;
- default:
- ;
- }
-}
-
-void QNetworkReplyHttpImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState)
-{
- if (sessionState == QNetworkSession::Disconnected
- && state != Idle && state != Reconnecting) {
- error(QNetworkReplyImpl::NetworkSessionFailedError,
- QCoreApplication::translate("QNetworkReply", "Network session error."));
- finished();
- }
-}
-
-void QNetworkReplyHttpImplPrivate::_q_networkSessionFailed()
-{
- // Abort waiting and working replies.
- if (state == WaitingForSession || state == Working) {
- state = Working;
- QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
- QString errorStr;
- if (session)
- errorStr = session->errorString();
- else
- errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
- error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
- finished();
- }
-}
-
-void QNetworkReplyHttpImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies)
-{
- if (request.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) {
- if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) {
- // Abort waiting and working replies.
- if (state == WaitingForSession || state == Working) {
- state = Working;
- error(QNetworkReply::BackgroundRequestNotAllowedError,
- QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
- finished();
- }
- // ### if canResume(), then we could resume automatically
- }
- }
-
-}
-#endif
-
-
// need to have this function since the reply is a private member variable
// and the special backends need to access this.
void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
@@ -2173,10 +2108,10 @@ QNonContiguousByteDevice* QNetworkReplyHttpImplPrivate::createUploadByteDevice()
// We want signal emissions only for normal asynchronous uploads
if (!synchronous)
- QObject::connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)),
+ QObject::connect(uploadByteDevice.get(), SIGNAL(readProgress(qint64,qint64)),
q, SLOT(emitReplyUploadProgress(qint64,qint64)));
- return uploadByteDevice.data();
+ return uploadByteDevice.get();
}
void QNetworkReplyHttpImplPrivate::_q_finished()
@@ -2190,38 +2125,19 @@ void QNetworkReplyHttpImplPrivate::finished()
Q_Q(QNetworkReplyHttpImpl);
if (transferTimeout)
transferTimeout->stop();
- if (state == Finished || state == Aborted || state == WaitingForSession)
+ if (state == Finished || state == Aborted)
return;
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- if (preMigrationDownloaded != Q_INT64_C(-1))
- totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ const qint64 totalSize = totalSizeOpt.value_or(-1);
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_ASSERT(managerPrivate);
- QSharedPointer<QNetworkSession> session = managerPrivate->getNetworkSession();
- if (!QNetworkStatusMonitor::isEnabled() && session && session->state() == QNetworkSession::Roaming &&
- state == Working && errorCode != QNetworkReply::OperationCanceledError) {
- // only content with a known size will fail with a temporary network failure error
- if (!totalSize.isNull()) {
- if (bytesDownloaded != totalSize) {
- if (migrateBackend()) {
- // either we are migrating or the request is finished/aborted
- if (state == Reconnecting || state == WaitingForSession) {
- return; // exit early if we are migrating.
- }
- } else {
- error(QNetworkReply::TemporaryNetworkFailureError,
- QNetworkReply::tr("Temporary network failure."));
- }
- }
- }
- }
-#endif
-
- // if we don't know the total size of or we received everything save the cache
- if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ // if we don't know the total size of or we received everything save the cache.
+ // If the data is compressed then this is done in readData()
+ if ((totalSize == -1 || bytesDownloaded == totalSize)
+ && !decompressHelper.isValid()) {
completeCacheSave();
+ }
// We check for errorCode too as in case of SSL handshake failure, we still
// get the HTTP redirect status code (301, 303 etc)
@@ -2231,10 +2147,10 @@ void QNetworkReplyHttpImplPrivate::finished()
state = Finished;
q->setFinished(true);
- if (totalSize.isNull() || totalSize == -1) {
+ if (totalSize == -1) {
emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
} else {
- emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSize);
}
if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
@@ -2255,7 +2171,9 @@ void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, c
Q_Q(QNetworkReplyHttpImpl);
// Can't set and emit multiple errors.
if (errorCode != QNetworkReply::NoError) {
- qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
+ // But somewhat unavoidable if we have cancelled the request:
+ if (errorCode != QNetworkReply::OperationCanceledError)
+ qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
return;
}
@@ -2265,7 +2183,7 @@ void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, c
// note: might not be a good idea, since users could decide to delete us
// which would delete the backend too...
// maybe we should protect the backend
- emit q->error(code);
+ emit q->errorOccurred(code);
}
void QNetworkReplyHttpImplPrivate::_q_metaDataChanged()
@@ -2276,61 +2194,21 @@ void QNetworkReplyHttpImplPrivate::_q_metaDataChanged()
// 1. do we have cookies?
// 2. are we allowed to set them?
Q_ASSERT(manager);
- const auto it = cookedHeaders.constFind(QNetworkRequest::SetCookieHeader);
- if (it != cookedHeaders.cend()
+
+ const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
+ headers().values(QHttpHeaders::WellKnownHeader::SetCookie));
+ const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
+ if (!cookies.empty()
&& request.attribute(QNetworkRequest::CookieSaveControlAttribute,
QNetworkRequest::Automatic).toInt() == QNetworkRequest::Automatic) {
QNetworkCookieJar *jar = manager->cookieJar();
if (jar) {
- QList<QNetworkCookie> cookies =
- qvariant_cast<QList<QNetworkCookie> >(it.value());
jar->setCookiesFromUrl(cookies, url);
}
}
emit q->metaDataChanged();
}
-/*
- Migrates the backend of the QNetworkReply to a new network connection if required. Returns
- true if the reply is migrated or it is not required; otherwise returns \c false.
-*/
-bool QNetworkReplyHttpImplPrivate::migrateBackend()
-{
- Q_Q(QNetworkReplyHttpImpl);
-
- // Network reply is already finished or aborted, don't need to migrate.
- if (state == Finished || state == Aborted)
- return true;
-
- // Backend does not support resuming download.
- if (!canResume())
- return false;
-
- // Request has outgoing data, not migrating.
- if (outgoingData)
- return false;
-
- // Request is serviced from the cache, don't need to migrate.
- if (cacheLoadDevice)
- return true;
-
- state = Reconnecting;
-
- cookedHeaders.clear();
- rawHeaders.clear();
-
- preMigrationDownloaded = bytesDownloaded;
-
- setResumeOffset(bytesDownloaded);
-
- emit q->abortHttpRequest();
-
- QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
-
- return true;
-}
-
-
void QNetworkReplyHttpImplPrivate::createCache()
{
// check if we can save and if we're allowed to
@@ -2388,3 +2266,5 @@ void QNetworkReplyHttpImplPrivate::completeCacheSave()
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreplyhttpimpl_p.cpp"
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index dec0c4c589..e00c43bdb3 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYHTTPIMPL_P_H
#define QNETWORKREPLYHTTPIMPL_P_H
@@ -66,12 +30,17 @@
#include <private/qhttpnetworkrequest_p.h>
#include <private/qnetworkreply_p.h>
#include <QtNetwork/QNetworkProxy>
-#include <QtNetwork/QNetworkSession>
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
#endif
+Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
+
+#include <private/qdecompresshelper_p.h>
+
+#include <memory>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -97,26 +66,16 @@ public:
Q_DECLARE_PRIVATE(QNetworkReplyHttpImpl)
Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
- Q_PRIVATE_SLOT(d_func(), bool start(const QNetworkRequest &))
Q_PRIVATE_SLOT(d_func(), void _q_cacheLoadReadyRead())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
Q_PRIVATE_SLOT(d_func(), void _q_transferTimedOut())
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected())
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed())
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))
-#endif
Q_PRIVATE_SLOT(d_func(), void _q_finished())
Q_PRIVATE_SLOT(d_func(), void _q_error(QNetworkReply::NetworkError, const QString &))
// From reply
Q_PRIVATE_SLOT(d_func(), void replyDownloadData(QByteArray))
Q_PRIVATE_SLOT(d_func(), void replyFinished())
- Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool, QSharedPointer<char>,
- qint64, qint64, bool))
Q_PRIVATE_SLOT(d_func(), void replyDownloadProgressSlot(qint64,qint64))
Q_PRIVATE_SLOT(d_func(), void httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *))
Q_PRIVATE_SLOT(d_func(), void httpError(QNetworkReply::NetworkError, const QString &))
@@ -162,10 +121,6 @@ signals:
class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate
{
-#if QT_CONFIG(bearermanagement)
- bool startWaitForSession(QSharedPointer<QNetworkSession> &session);
-#endif
-
public:
static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio);
@@ -173,7 +128,6 @@ public:
QNetworkReplyHttpImplPrivate();
~QNetworkReplyHttpImplPrivate();
- bool start(const QNetworkRequest &newHttpRequest);
void _q_startOperation();
void _q_cacheLoadReadyRead();
@@ -186,12 +140,6 @@ public:
void _q_transferTimedOut();
void setupTransferTimeout();
-#ifndef QT_NO_BEARERMANAGEMENT
- void _q_networkSessionConnected();
- void _q_networkSessionFailed();
- void _q_networkSessionStateChanged(QNetworkSession::State);
- void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies);
-#endif
void _q_finished();
void finished();
@@ -215,11 +163,11 @@ public:
// upload
QNonContiguousByteDevice* createUploadByteDevice();
- QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+ std::shared_ptr<QNonContiguousByteDevice> uploadByteDevice;
qint64 uploadByteDevicePosition;
bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment
QIODevice *outgoingData;
- QSharedPointer<QRingBuffer> outgoingDataBuffer;
+ std::shared_ptr<QRingBuffer> outgoingDataBuffer;
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); // dup?
void onRedirected(const QUrl &redirectUrl, int httpStatus, int maxRedirectsRemainig);
void followRedirect();
@@ -246,14 +194,16 @@ public:
#endif
- bool migrateBackend();
bool canResume() const;
void setResumeOffset(quint64 offset);
quint64 resumeOffset;
- qint64 preMigrationDownloaded;
qint64 bytesDownloaded;
qint64 bytesBuffered;
+ // We use this to keep track of whether or not we need to emit readyRead
+ // when we deal with signal compression (delaying emission) + decompressing
+ // data (potentially receiving bytes that don't end up in the final output):
+ qint64 lastReadyReadEmittedSize = 0;
QTimer *transferTimeout;
@@ -265,8 +215,8 @@ public:
char* downloadZerocopyBuffer;
// Will be increased by HTTP thread:
- QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
- QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
+ std::shared_ptr<QAtomicInt> pendingDownloadDataEmissions;
+ std::shared_ptr<QAtomicInt> pendingDownloadProgressEmissions;
#ifndef QT_NO_SSL
@@ -277,6 +227,8 @@ public:
QNetworkRequest redirectRequest;
+ QDecompressHelper decompressHelper;
+
bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
void invalidateCache();
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
@@ -292,8 +244,8 @@ public:
// From HTTP thread:
void replyDownloadData(QByteArray);
void replyFinished();
- void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
- bool, QSharedPointer<char>, qint64, qint64, bool);
+ void replyDownloadMetaData(const QHttpHeaders &, int, const QString &,
+ bool, QSharedPointer<char>, qint64, qint64, bool, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index a43a29a239..b90ec1cc4c 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplyimpl_p.h"
#include "qnetworkaccessbackend_p.h"
@@ -45,19 +9,20 @@
#include "QtCore/qcoreapplication.h"
#include "QtCore/qdatetime.h"
#include "QtNetwork/qsslconfiguration.h"
-#include "QtNetwork/qnetworksession.h"
#include "qnetworkaccessmanager_p.h"
#include <QtCore/QCoreApplication>
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN_TAGGED(QSharedPointer<char>, QSharedPointer_char)
+
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
: backend(nullptr), outgoingData(nullptr),
copyDevice(nullptr),
cacheEnabled(false), cacheSaveDevice(nullptr),
notificationHandlingPaused(false),
- bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
+ bytesDownloaded(0), bytesUploaded(-1),
httpStatusCode(0),
state(Idle)
, downloadBufferReadPosition(0)
@@ -88,66 +53,15 @@ void QNetworkReplyImplPrivate::_q_startOperation()
return;
}
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_Q(QNetworkReplyImpl);
- // Do not start background requests if they are not allowed by session policy
- QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
- QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
- if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
- error(QNetworkReply::BackgroundRequestNotAllowedError,
- QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
- finished();
- return;
- }
-#endif
-
if (!backend->start()) {
-#ifndef QT_NO_BEARERMANAGEMENT
- // backend failed to start because the session state is not Connected.
- // QNetworkAccessManager will call _q_startOperation again for us when the session
- // state changes.
- state = WaitingForSession;
-
- if (session) {
- QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
- q, SLOT(_q_networkSessionFailed()));
-
- if (!session->isOpen()) {
- session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
- session->open();
- }
- } else {
- qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
- state = Working;
- error(QNetworkReplyImpl::NetworkSessionFailedError,
- QCoreApplication::translate("QNetworkReply", "Network session error."));
- finished();
- }
-#else
qWarning("Backend start failed");
state = Working;
error(QNetworkReplyImpl::UnknownNetworkError,
QCoreApplication::translate("QNetworkReply", "backend start error."));
finished();
-#endif
return;
- } else {
-#ifndef QT_NO_BEARERMANAGEMENT
- if (session) {
- QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)),
- q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection);
- }
-#endif
}
-#ifndef QT_NO_BEARERMANAGEMENT
- if (session) {
- //get notification of policy changes.
- QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
- q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
- }
-#endif
-
// Prepare timer for progress notifications
downloadProgressSignalChoke.start();
uploadProgressSignalChoke.invalidate();
@@ -176,7 +90,7 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
// FIXME Optimize to use download buffer if it is a QBuffer.
// Needs to be done where sendCacheContents() (?) of HTTP is emitting
// metaDataChanged ?
-
+ qint64 lastBytesDownloaded = bytesDownloaded;
forever {
qint64 bytesToRead = nextDownstreamBlockSize();
if (bytesToRead == 0)
@@ -187,13 +101,11 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
qint64 bytesActuallyRead = copyDevice->read(buffer.reserve(bytesToRead), bytesToRead);
if (bytesActuallyRead == -1) {
buffer.chop(bytesToRead);
- backendNotify(NotifyCopyFinished);
break;
}
buffer.chop(bytesToRead - bytesActuallyRead);
if (!copyDevice->isSequential() && copyDevice->atEnd()) {
- backendNotify(NotifyCopyFinished);
bytesDownloaded += bytesActuallyRead;
break;
}
@@ -206,18 +118,16 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
return;
}
- lastBytesDownloaded = bytesDownloaded;
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- if (preMigrationDownloaded != Q_INT64_C(-1))
- totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+
pauseNotificationHandling();
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
emit q->readyRead();
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
resumeNotificationHandling();
}
@@ -250,7 +160,7 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
if (!outgoingDataBuffer) {
// first call, create our buffer
- outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+ outgoingDataBuffer = std::make_shared<QRingBuffer>();
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
@@ -287,80 +197,6 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
}
}
-#ifndef QT_NO_BEARERMANAGEMENT
-void QNetworkReplyImplPrivate::_q_networkSessionConnected()
-{
- Q_Q(QNetworkReplyImpl);
-
- if (manager.isNull())
- return;
-
- QSharedPointer<QNetworkSession> session = manager->d_func()->getNetworkSession();
- if (!session)
- return;
-
- if (session->state() != QNetworkSession::Connected)
- return;
-
- switch (state) {
- case QNetworkReplyPrivate::Buffering:
- case QNetworkReplyPrivate::Working:
- case QNetworkReplyPrivate::Reconnecting:
- // Migrate existing downloads to new network connection.
- migrateBackend();
- break;
- case QNetworkReplyPrivate::WaitingForSession:
- // Start waiting requests.
- QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
- break;
- default:
- ;
- }
-}
-
-void QNetworkReplyImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState)
-{
- if (sessionState == QNetworkSession::Disconnected
- && state != Idle && state != Reconnecting) {
- error(QNetworkReplyImpl::NetworkSessionFailedError,
- QCoreApplication::translate("QNetworkReply", "Network session error."));
- finished();
- }
-}
-
-void QNetworkReplyImplPrivate::_q_networkSessionFailed()
-{
- // Abort waiting and working replies.
- if (state == WaitingForSession || state == Working) {
- state = Working;
- QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
- QString errorStr;
- if (session)
- errorStr = session->errorString();
- else
- errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
- error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
- finished();
- }
-}
-
-void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies)
-{
- if (backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) {
- if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) {
- // Abort waiting and working replies.
- if (state == WaitingForSession || state == Working) {
- state = Working;
- error(QNetworkReply::BackgroundRequestNotAllowedError,
- QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
- finished();
- }
- // ### if backend->canResume(), then we could resume automatically, however no backend supports resuming
- }
- }
-}
-#endif
-
void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
QIODevice *data)
{
@@ -380,7 +216,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
// The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
// Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
if (synchronousHttpAttribute.toBool() && outgoingData) {
- outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+ outgoingDataBuffer = std::make_shared<QRingBuffer>();
qint64 previousDataSize = 0;
do {
previousDataSize = outgoingDataBuffer->size();
@@ -408,7 +244,10 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
if (bufferingDisallowed) {
// if a valid content-length header for the request was supplied, we can disable buffering
// if not, we will buffer anyway
- if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+
+ if (sizeOpt) {
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
} else {
state = Buffering;
@@ -446,27 +285,21 @@ void QNetworkReplyImplPrivate::handleNotifications()
if (notificationHandlingPaused)
return;
- for (InternalNotifications notification : qExchange(pendingNotifications, {})) {
+ for (InternalNotifications notification : std::exchange(pendingNotifications, {})) {
if (state != Working)
return;
switch (notification) {
case NotifyDownstreamReadyWrite:
- if (copyDevice)
+ if (copyDevice) {
_q_copyReadyRead();
- else
- backend->downstreamReadyWrite();
- break;
-
- case NotifyCloseDownstreamChannel:
- backend->closeDownstreamChannel();
- break;
-
- case NotifyCopyFinished: {
- QIODevice *dev = qExchange(copyDevice, nullptr);
- backend->copyFinished(dev);
+ } else if (backend) {
+ if (backend->bytesAvailable() > 0)
+ readFromBackend();
+ else if (backend->wantToRead())
+ readFromBackend();
+ }
break;
}
- }
}
}
@@ -591,7 +424,8 @@ void QNetworkReplyImplPrivate::initCacheSaveDevice()
// save the meta data
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
- metaData = backend->fetchCacheMetaData(metaData);
+ // @todo @future: fetchCacheMetaData is not currently implemented in any backend, but can be useful again in the future
+ // metaData = backend->fetchCacheMetaData(metaData);
// save the redirect request also in the cache
QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
@@ -628,7 +462,7 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
}
qint64 bytesWritten = 0;
- for (int i = 0; i < data.bufferCount(); i++) {
+ for (qsizetype i = 0; i < data.bufferCount(); ++i) {
QByteArray const &item = data[i];
if (cacheSaveDevice)
@@ -640,7 +474,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
data.clear();
bytesDownloaded += bytesWritten;
- lastBytesDownloaded = bytesDownloaded;
appendDownstreamDataSignalEmissions();
}
@@ -649,19 +482,17 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
{
Q_Q(QNetworkReplyImpl);
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- if (preMigrationDownloaded != Q_INT64_C(-1))
- totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
pauseNotificationHandling();
// important: At the point of this readyRead(), the data parameter list must be empty,
// else implicit sharing will trigger memcpy when the user is reading data!
emit q->readyRead();
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
resumeNotificationHandling();
@@ -680,7 +511,7 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
// read until EOF from data
if (Q_UNLIKELY(copyDevice)) {
qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
- "backend probly needs to be fixed");
+ "backend probably needs to be fixed");
return;
}
@@ -692,21 +523,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
_q_copyReadyRead();
}
-void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
-{
- Q_UNUSED(data)
- // TODO implement
-
- // TODO call
-
- qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
-}
-
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
{
Q_Q(QNetworkReplyImpl);
@@ -719,7 +535,7 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
downloadBufferCurrentSize = 0;
downloadBufferMaximumSize = size;
downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
- downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
+ downloadBufferPointer = QSharedPointer<char>(downloadBuffer, [](auto p) { delete[] p; });
q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
}
@@ -750,21 +566,16 @@ void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesRe
initCacheSaveDevice();
if (cacheSaveDevice && bytesReceived == bytesTotal) {
-// if (lastBytesDownloaded == -1)
-// lastBytesDownloaded = 0;
-// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
-
// Write everything in one go if we use a download buffer. might be more performant.
cacheSaveDevice->write(downloadBuffer, bytesTotal);
}
bytesDownloaded = bytesReceived;
- lastBytesDownloaded = bytesReceived;
downloadBufferCurrentSize = bytesReceived;
// Only emit readyRead when actual data is there
- // emit readyRead before downloadProgress incase this will cause events to be
+ // emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
if (bytesDownloaded > 0)
emit q->readyRead();
@@ -778,37 +589,14 @@ void QNetworkReplyImplPrivate::finished()
{
Q_Q(QNetworkReplyImpl);
- if (state == Finished || state == Aborted || state == WaitingForSession)
+ if (state == Finished || state == Aborted)
return;
pauseNotificationHandling();
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- if (preMigrationDownloaded != Q_INT64_C(-1))
- totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ const auto totalSize = totalSizeOpt.value_or(-1);
- if (!manager.isNull()) {
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> session (manager->d_func()->getNetworkSession());
- if (session && session->state() == QNetworkSession::Roaming &&
- state == Working && errorCode != QNetworkReply::OperationCanceledError) {
- // only content with a known size will fail with a temporary network failure error
- if (!totalSize.isNull()) {
- if (bytesDownloaded != totalSize) {
- if (migrateBackend()) {
- // either we are migrating or the request is finished/aborted
- if (state == Reconnecting || state == WaitingForSession) {
- resumeNotificationHandling();
- return; // exit early if we are migrating.
- }
- } else {
- error(QNetworkReply::TemporaryNetworkFailureError,
- QNetworkReply::tr("Temporary network failure."));
- }
- }
- }
- }
-#endif
- }
resumeNotificationHandling();
state = Finished;
@@ -817,10 +605,10 @@ void QNetworkReplyImplPrivate::finished()
pendingNotifications.clear();
pauseNotificationHandling();
- if (totalSize.isNull() || totalSize == -1) {
+ if (totalSize == -1) {
emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
} else {
- emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSize);
}
if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
@@ -828,7 +616,7 @@ void QNetworkReplyImplPrivate::finished()
resumeNotificationHandling();
// if we don't know the total size of or we received everything save the cache
- if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ if (totalSize == -1 || bytesDownloaded == totalSize)
completeCacheSave();
// note: might not be a good idea, since users could decide to delete us
@@ -855,7 +643,7 @@ void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const
// note: might not be a good idea, since users could decide to delete us
// which would delete the backend too...
// maybe we should protect the backend
- emit q->error(code);
+ emit q->errorOccurred(code);
}
void QNetworkReplyImplPrivate::metaDataChanged()
@@ -864,14 +652,14 @@ void QNetworkReplyImplPrivate::metaDataChanged()
// 1. do we have cookies?
// 2. are we allowed to set them?
if (!manager.isNull()) {
- const auto it = cookedHeaders.constFind(QNetworkRequest::SetCookieHeader);
- if (it != cookedHeaders.cend()
+ const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
+ headers().values(QHttpHeaders::WellKnownHeader::SetCookie));
+ const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
+ if (!cookies.empty()
&& request.attribute(QNetworkRequest::CookieSaveControlAttribute,
QNetworkRequest::Automatic).toInt() == QNetworkRequest::Automatic) {
QNetworkCookieJar *jar = manager->cookieJar();
if (jar) {
- QList<QNetworkCookie> cookies =
- qvariant_cast<QList<QNetworkCookie> >(it.value());
jar->setCookiesFromUrl(cookies, url);
}
}
@@ -903,6 +691,33 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
#endif
}
+void QNetworkReplyImplPrivate::readFromBackend()
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!backend)
+ return;
+
+ if (backend->ioFeatures() & QNetworkAccessBackend::IOFeature::ZeroCopy) {
+ if (backend->bytesAvailable())
+ emit q->readyRead();
+ } else {
+ bool anyBytesRead = false;
+ while (backend->bytesAvailable()
+ && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
+ qint64 toRead = qMin(nextDownstreamBlockSize(), backend->bytesAvailable());
+ if (toRead == 0)
+ toRead = 16 * 1024; // try to read something
+ char *data = buffer.reserve(toRead);
+ qint64 bytesRead = backend->read(data, toRead);
+ Q_ASSERT(bytesRead <= toRead);
+ buffer.chop(toRead - bytesRead);
+ anyBytesRead |= bytesRead > 0;
+ }
+ if (anyBytesRead)
+ emit q->readyRead();
+ }
+}
+
QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
: QNetworkReply(*new QNetworkReplyImplPrivate, parent)
{
@@ -935,8 +750,6 @@ void QNetworkReplyImpl::abort()
// call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
- if (d->state == QNetworkReplyPrivate::WaitingForSession)
- d->state = QNetworkReplyPrivate::Working;
d->finished();
d->state = QNetworkReplyPrivate::Aborted;
@@ -956,7 +769,7 @@ void QNetworkReplyImpl::close()
// stop the download
if (d->backend)
- d->backend->closeDownstreamChannel();
+ d->backend->close();
if (d->copyDevice)
disconnect(d->copyDevice, nullptr, this, nullptr);
@@ -980,21 +793,16 @@ qint64 QNetworkReplyImpl::bytesAvailable() const
qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
return QNetworkReply::bytesAvailable() + maxAvail;
}
-
- return QNetworkReply::bytesAvailable();
+ return QNetworkReply::bytesAvailable() + (d->backend ? d->backend->bytesAvailable() : 0);
}
void QNetworkReplyImpl::setReadBufferSize(qint64 size)
{
Q_D(QNetworkReplyImpl);
- if (size > d->readBufferMaxSize &&
- size > d->buffer.size())
- d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
-
+ qint64 oldMaxSize = d->readBufferMaxSize;
QNetworkReply::setReadBufferSize(size);
-
- if (d->backend)
- d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
+ if (size > oldMaxSize && size > d->buffer.size())
+ d->readFromBackend();
}
#ifndef QT_NO_SSL
@@ -1002,7 +810,7 @@ void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &config
{
Q_D(const QNetworkReplyImpl);
if (d->backend)
- d->backend->fetchSslConfiguration(configuration);
+ configuration = d->backend->sslConfiguration();
}
void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
@@ -1034,6 +842,36 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
{
Q_D(QNetworkReplyImpl);
+ if (d->backend
+ && d->backend->ioFeatures().testFlag(QNetworkAccessBackend::IOFeature::ZeroCopy)) {
+ qint64 bytesRead = 0;
+ while (d->backend->bytesAvailable()) {
+ QByteArrayView view = d->backend->readPointer();
+ if (view.size()) {
+ qint64 bytesToCopy = qMin(qint64(view.size()), maxlen - bytesRead);
+ memcpy(data + bytesRead, view.data(), bytesToCopy); // from zero to one copy
+
+ // We might have to cache this
+ if (d->cacheEnabled && !d->cacheSaveDevice)
+ d->initCacheSaveDevice();
+ if (d->cacheEnabled && d->cacheSaveDevice)
+ d->cacheSaveDevice->write(view.data(), view.size());
+
+ bytesRead += bytesToCopy;
+ d->backend->advanceReadPointer(bytesToCopy);
+ } else {
+ break;
+ }
+ }
+
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ emit downloadProgress(bytesRead, totalSizeOpt.value_or(-1));
+ return bytesRead;
+ } else if (d->backend && d->backend->bytesAvailable()) {
+ return d->backend->read(data, maxlen);
+ }
+
// Special case code if we have the "zero copy" download buffer
if (d->downloadBuffer) {
qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
@@ -1067,76 +905,6 @@ bool QNetworkReplyImpl::event(QEvent *e)
return QObject::event(e);
}
-/*
- Migrates the backend of the QNetworkReply to a new network connection if required. Returns
- true if the reply is migrated or it is not required; otherwise returns \c false.
-*/
-bool QNetworkReplyImplPrivate::migrateBackend()
-{
- Q_Q(QNetworkReplyImpl);
-
- // Network reply is already finished or aborted, don't need to migrate.
- if (state == Finished || state == Aborted)
- return true;
-
- // Request has outgoing data, not migrating.
- if (outgoingData)
- return false;
-
- // Request is serviced from the cache, don't need to migrate.
- if (copyDevice)
- return true;
-
- // Backend does not support resuming download.
- if (backend && !backend->canResume())
- return false;
-
- state = QNetworkReplyPrivate::Reconnecting;
-
- cookedHeaders.clear();
- rawHeaders.clear();
-
- preMigrationDownloaded = bytesDownloaded;
-
- delete backend;
- backend = manager->d_func()->findBackend(operation, request);
-
- if (backend) {
- backend->setParent(q);
- backend->reply = this;
- backend->setResumeOffset(bytesDownloaded);
- }
-
- QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
-
- return true;
-}
-
-QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
- const QNetworkRequest &req,
- QNetworkAccessManager::Operation op)
-: QNetworkReply(parent)
-{
- setRequest(req);
- setUrl(req.url());
- setOperation(op);
- setFinished(true);
-
- qRegisterMetaType<QNetworkReply::NetworkError>();
-
- QString msg = QCoreApplication::translate("QNetworkAccessManager",
- "Network access is disabled.");
- setError(UnknownNetworkError, msg);
-
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
-}
-
-QDisabledNetworkReply::~QDisabledNetworkReply()
-{
-}
-
QT_END_NAMESPACE
#include "moc_qnetworkreplyimpl_p.cpp"
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index 8cec79541a..9648b8b57a 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYIMPL_P_H
#define QNETWORKREPLYIMPL_P_H
@@ -62,7 +26,8 @@
#include "private/qringbuffer_p.h"
#include "private/qbytedata_p.h"
#include <QSharedPointer>
-#include <QtNetwork/QNetworkSession>
+
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -92,12 +57,6 @@ public:
Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
-#ifndef QT_NO_BEARERMANAGEMENT
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected())
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed())
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))
- Q_PRIVATE_SLOT(d_func(), void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies))
-#endif
#ifndef QT_NO_SSL
protected:
@@ -113,8 +72,6 @@ class QNetworkReplyImplPrivate: public QNetworkReplyPrivate
public:
enum InternalNotifications {
NotifyDownstreamReadyWrite,
- NotifyCloseDownstreamChannel,
- NotifyCopyFinished
};
QNetworkReplyImplPrivate();
@@ -124,12 +81,6 @@ public:
void _q_copyReadChannelFinished();
void _q_bufferOutgoingData();
void _q_bufferOutgoingDataFinished();
-#ifndef QT_NO_BEARERMANAGEMENT
- void _q_networkSessionConnected();
- void _q_networkSessionFailed();
- void _q_networkSessionStateChanged(QNetworkSession::State);
- void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies);
-#endif
void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
QIODevice *outgoingData);
@@ -152,7 +103,6 @@ public:
void appendDownstreamDataSignalEmissions();
void appendDownstreamData(QByteDataBuffer &data);
void appendDownstreamData(QIODevice *data);
- void appendDownstreamData(const QByteArray &data);
void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
char* getDownloadBuffer(qint64 size);
@@ -165,14 +115,14 @@ public:
void encrypted();
void sslErrors(const QList<QSslError> &errors);
+ void readFromBackend();
+
QNetworkAccessBackend *backend;
QIODevice *outgoingData;
- QSharedPointer<QRingBuffer> outgoingDataBuffer;
+ std::shared_ptr<QRingBuffer> outgoingDataBuffer;
QIODevice *copyDevice;
QAbstractNetworkCache *networkCache() const;
- bool migrateBackend();
-
bool cacheEnabled;
QIODevice *cacheSaveDevice;
@@ -186,9 +136,7 @@ public:
#endif
qint64 bytesDownloaded;
- qint64 lastBytesDownloaded;
qint64 bytesUploaded;
- qint64 preMigrationDownloaded;
QString httpReasonPhrase;
int httpStatusCode;
@@ -207,22 +155,6 @@ public:
};
Q_DECLARE_TYPEINFO(QNetworkReplyImplPrivate::InternalNotifications, Q_PRIMITIVE_TYPE);
-class QDisabledNetworkReply : public QNetworkReply
-{
- Q_OBJECT
-
-public:
- QDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
- QNetworkAccessManager::Operation op);
- ~QDisabledNetworkReply();
-
- void abort() override { }
-protected:
- qint64 readData(char *, qint64) override { return -1; }
-};
-
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSharedPointer<char>)
-
#endif
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index f28b8415d1..7d2b6a701e 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplywasmimpl_p.h"
#include "qnetworkrequest.h"
@@ -45,184 +9,52 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
+#include <QtCore/private/qeventdispatcher_wasm_p.h>
+#include <QtCore/private/qoffsetstringarray_p.h>
+#include <QtCore/private/qtools_p.h>
#include <private/qnetworkaccessmanager_p.h>
#include <private/qnetworkfile_p.h>
#include <emscripten.h>
-#include <emscripten/bind.h>
-#include <emscripten/val.h>
+#include <emscripten/fetch.h>
QT_BEGIN_NAMESPACE
-using namespace emscripten;
-
-static void q_requestErrorCallback(val event)
-{
- if (event.isNull() || event.isUndefined())
- return;
-
- val xhr = event["target"];
- if (xhr.isNull() || xhr.isUndefined())
- return;
-
- quintptr func = xhr["data-handler"].as<quintptr>();
- QNetworkReplyWasmImplPrivate *reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(func);
- Q_ASSERT(reply);
-
- int statusCode = xhr["status"].as<int>();
-
- QString reasonStr = QString::fromStdString(xhr["statusText"].as<std::string>());
-
- reply->setReplyAttributes(func, statusCode, reasonStr);
-
- if (statusCode >= 400 && !reasonStr.isEmpty())
- reply->emitReplyError(reply->statusCodeFromHttp(statusCode, reply->request.url()), reasonStr);
-}
-
-static void q_progressCallback(val event)
-{
- if (event.isNull() || event.isUndefined())
- return;
-
- val xhr = event["target"];
- if (xhr.isNull() || xhr.isUndefined())
- return;
-
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(xhr["data-handler"].as<quintptr>());
- Q_ASSERT(reply);
-
- if (xhr["status"].as<int>() < 400)
- reply->emitDataReadProgress(event["loaded"].as<int>(), event["total"].as<int>());
-}
-
-static void q_loadCallback(val event)
-{
- if (event.isNull() || event.isUndefined())
- return;
-
- val xhr = event["target"];
- if (xhr.isNull() || xhr.isUndefined())
- return;
-
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(xhr["data-handler"].as<quintptr>());
- Q_ASSERT(reply);
-
- int status = xhr["status"].as<int>();
- if (status >= 300) {
- q_requestErrorCallback(event);
- return;
- }
- QString statusText = QString::fromStdString(xhr["statusText"].as<std::string>());
- int readyState = xhr["readyState"].as<int>();
-
- if (status == 200 || status == 203) {
- QString responseString;
- const std::string responseType = xhr["responseType"].as<std::string>();
- if (responseType.length() == 0 || responseType == "document" || responseType == "text") {
- responseString = QString::fromStdWString(xhr["responseText"].as<std::wstring>());
- } else if (responseType == "json") {
- responseString =
- QString::fromStdWString(val::global("JSON").call<std::wstring>("stringify", xhr["response"]));
- } else if (responseType == "arraybuffer" || responseType == "blob") {
- // handle this data in the FileReader, triggered by the call to readAsArrayBuffer
- val blob = xhr["response"];
- if (blob.isNull() || blob.isUndefined())
- return;
-
- val reader = val::global("FileReader").new_();
- if (reader.isNull() || reader.isUndefined())
- return;
-
- reader.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_readBinary"));
- reader.set("data-handler", xhr["data-handler"]);
-
- reader.call<void>("readAsArrayBuffer", blob);
- val::global("Module").delete_(reader);
- }
-
-
- if (readyState == 4) { // done
- reply->setReplyAttributes(xhr["data-handler"].as<quintptr>(), status, statusText);
- if (!responseString.isEmpty()) {
- QByteArray responseStringArray = responseString.toUtf8();
- reply->dataReceived(responseStringArray, responseStringArray.size());
- }
- }
- }
- if (status >= 400 && !statusText.isEmpty())
- reply->emitReplyError(reply->statusCodeFromHttp(status, reply->request.url()), statusText);
-}
-
-static void q_responseHeadersCallback(val event)
+using namespace Qt::StringLiterals;
+
+namespace {
+
+static constexpr auto BannedHeaders = qOffsetStringArray(
+ "accept-charset",
+ "accept-encoding",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "connection",
+ "content-length",
+ "cookie",
+ "cookie2",
+ "date",
+ "dnt",
+ "expect",
+ "host",
+ "keep-alive",
+ "origin",
+ "referer",
+ "te",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "via"
+);
+
+bool isUnsafeHeader(QLatin1StringView header) noexcept
{
- if (event.isNull() || event.isUndefined())
- return;
-
- val xhr = event["target"];
- if (xhr.isNull() || xhr.isUndefined())
- return;
-
- if (xhr["readyState"].as<int>() == 2) { // HEADERS_RECEIVED
- std::string responseHeaders = xhr.call<std::string>("getAllResponseHeaders");
- if (!responseHeaders.empty()) {
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(xhr["data-handler"].as<quintptr>());
- Q_ASSERT(reply);
-
- reply->headersReceived(QString::fromStdString(responseHeaders));
- }
- }
-}
-
-static void q_readBinary(val event)
-{
- if (event.isNull() || event.isUndefined())
- return;
-
- val fileReader = event["target"];
- if (fileReader.isNull() || fileReader.isUndefined())
- return;
-
- QNetworkReplyWasmImplPrivate *reply =
- reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fileReader["data-handler"].as<quintptr>());
- Q_ASSERT(reply);
-
- if (reply->state == QNetworkReplyPrivate::Finished || reply->state == QNetworkReplyPrivate::Aborted)
- return;
-
- // Set up source typed array
- val result = fileReader["result"]; // ArrayBuffer
- if (result.isNull() || result.isUndefined())
- return;
-
- val Uint8Array = val::global("Uint8Array");
- val sourceTypedArray = Uint8Array.new_(result);
-
- // Allocate and set up destination typed array
- const quintptr size = result["byteLength"].as<quintptr>();
- QByteArray buffer(size, Qt::Uninitialized);
-
- val destinationTypedArray = Uint8Array.new_(val::module_property("HEAPU8")["buffer"],
- reinterpret_cast<quintptr>(buffer.data()), size);
- destinationTypedArray.call<void>("set", sourceTypedArray);
- reply->dataReceived(buffer, buffer.size());
-
- event.delete_(fileReader);
- Uint8Array.delete_(sourceTypedArray);
-
- QCoreApplication::processEvents();
-}
-
-EMSCRIPTEN_BINDINGS(qtNetworkModule) {
- function("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback);
- function("qt_QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback);
- function("qt_QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback);
- function("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback);
- function("qt_QNetworkReplyWasmImplPrivate_readBinary", q_readBinary);
+ return header.startsWith("proxy-"_L1, Qt::CaseInsensitive)
+ || header.startsWith("sec-"_L1, Qt::CaseInsensitive)
+ || BannedHeaders.contains(header, Qt::CaseInsensitive);
}
+} // namespace
QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
: QNetworkReplyPrivate()
@@ -231,16 +63,28 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
, downloadBufferCurrentSize(0)
, totalDownloadSize(0)
, percentFinished(0)
+ , m_fetch(nullptr)
+ , m_fetchContext(nullptr)
{
}
QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate()
{
- m_xhr.set("onerror", val::null());
- m_xhr.set("onload", val::null());
- m_xhr.set("onprogress", val::null());
- m_xhr.set("onreadystatechange", val::null());
- m_xhr.set("data-handler", val::null());
+
+ if (m_fetchContext) { // fetch has been initiated
+ std::unique_lock lock{ m_fetchContext->mutex };
+
+ if (m_fetchContext->state == FetchContext::State::SCHEDULED
+ || m_fetchContext->state == FetchContext::State::SENT
+ || m_fetchContext->state == FetchContext::State::CANCELED) {
+ m_fetchContext->reply = nullptr;
+ m_fetchContext->state = FetchContext::State::TO_BE_DESTROYED;
+ } else if (m_fetchContext->state == FetchContext::State::FINISHED) {
+ lock.unlock();
+ delete m_fetchContext;
+ }
+ }
+
}
QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
@@ -252,6 +96,9 @@ QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
{
+ if (isRunning())
+ abort();
+ close();
}
QByteArray QNetworkReplyWasmImpl::methodName() const
@@ -278,23 +125,44 @@ QByteArray QNetworkReplyWasmImpl::methodName() const
void QNetworkReplyWasmImpl::close()
{
+ Q_D(QNetworkReplyWasmImpl);
+
+ if (d->state != QNetworkReplyPrivate::Aborted &&
+ d->state != QNetworkReplyPrivate::Finished &&
+ d->state != QNetworkReplyPrivate::Idle) {
+ d->state = QNetworkReplyPrivate::Finished;
+ d->setCanceled();
+ }
+ emscripten_fetch_close(d->m_fetch);
QNetworkReply::close();
- setFinished(true);
- emit finished();
}
void QNetworkReplyWasmImpl::abort()
{
- Q_D( QNetworkReplyWasmImpl);
+ Q_D(QNetworkReplyWasmImpl);
+
if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
return;
- setError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+ d->state = QNetworkReplyPrivate::Aborted;
+ d->setCanceled();
+}
- d->doAbort();
+void QNetworkReplyWasmImplPrivate::setCanceled()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ {
+ if (m_fetchContext) {
+ std::scoped_lock lock{ m_fetchContext->mutex };
+ if (m_fetchContext->state == FetchContext::State::SCHEDULED
+ || m_fetchContext->state == FetchContext::State::SENT)
+ m_fetchContext->state = FetchContext::State::CANCELED;
+ }
+ }
- close();
- d->state = QNetworkReplyPrivate::Aborted;
+ emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+ q->setFinished(true);
+ emit q->finished();
}
qint64 QNetworkReplyWasmImpl::bytesAvailable() const
@@ -371,9 +239,8 @@ void QNetworkReplyWasmImplPrivate::setReplyAttributes(quintptr data, int statusC
handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusReason);
}
-void QNetworkReplyWasmImplPrivate::doAbort() const
-{
- m_xhr.call<void>("abort");
+constexpr int getArraySize (int factor) {
+ return 2 * factor + 1;
}
void QNetworkReplyWasmImplPrivate::doSendRequest()
@@ -381,50 +248,103 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
Q_Q(QNetworkReplyWasmImpl);
totalDownloadSize = 0;
- m_xhr = val::global("XMLHttpRequest").new_();
- std::string verb = q->methodName().toStdString();
+ emscripten_fetch_attr_t attr;
+ emscripten_fetch_attr_init(&attr);
+ qstrncpy(attr.requestMethod, q->methodName().constData(), 32); // requestMethod is char[32] in emscripten
- m_xhr.call<void>("open", verb, request.url().toString().toStdString());
+ attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
- m_xhr.set("onerror", val::module_property("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback"));
- m_xhr.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_loadCallback"));
- m_xhr.set("onprogress", val::module_property("qt_QNetworkReplyWasmImplPrivate_progressCallback"));
- m_xhr.set("onreadystatechange", val::module_property("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback"));
+ QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
+ (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
- m_xhr.set("data-handler", val(quintptr(reinterpret_cast<void *>(this))));
+ if (CacheLoadControlAttribute == QNetworkRequest::AlwaysCache) {
+ attr.attributes += EMSCRIPTEN_FETCH_NO_DOWNLOAD;
+ }
+ if (CacheLoadControlAttribute == QNetworkRequest::PreferCache) {
+ attr.attributes += EMSCRIPTEN_FETCH_APPEND;
+ }
- QByteArray contentType = request.rawHeader("Content-Type");
+ if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork ||
+ request.attribute(QNetworkRequest::CacheSaveControlAttribute, false).toBool()) {
+ attr.attributes -= EMSCRIPTEN_FETCH_PERSIST_FILE;
+ }
- // handle extra data
- val dataToSend = val::null();
- QByteArray extraData;
+ attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute, false).toBool();
+ attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded;
+ attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed;
+ attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
+ attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange;
+ attr.timeoutMSecs = request.transferTimeout();
+
+ m_fetchContext = new FetchContext(this);;
+ attr.userData = static_cast<void *>(m_fetchContext);
+ if (outgoingData) { // data from post request
+ m_fetchContext->requestData = outgoingData->readAll(); // is there a size restriction here?
+ if (!m_fetchContext->requestData.isEmpty()) {
+ attr.requestData = m_fetchContext->requestData.data();
+ attr.requestDataSize = m_fetchContext->requestData.size();
+ }
+ }
+
+ QEventDispatcherWasm::runOnMainThread([attr, fetchContext = m_fetchContext]() mutable {
+ std::unique_lock lock{ fetchContext->mutex };
+ if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ }
+ const auto reply = fetchContext->reply;
+ const auto &request = reply->request;
+
+ QByteArray userName, password;
+ if (!request.url().userInfo().isEmpty()) {
+ userName = request.url().userName().toUtf8();
+ password = request.url().password().toUtf8();
+ attr.userName = userName.constData();
+ attr.password = password.constData();
+ }
- if (outgoingData) // data from post request
- extraData = outgoingData->readAll();
+ QList<QByteArray> headersData = request.rawHeaderList();
+ int arrayLength = getArraySize(headersData.count());
+ const char *customHeaders[arrayLength];
+ QStringList trimmedHeaders;
+ if (headersData.count() > 0) {
+ int i = 0;
+ for (const auto &headerName : headersData) {
+ if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
+ trimmedHeaders.push_back(QString::fromLatin1(headerName));
+ } else {
+ customHeaders[i++] = headerName.constData();
+ customHeaders[i++] = request.rawHeader(headerName).constData();
+ }
+ }
+ if (!trimmedHeaders.isEmpty()) {
+ qWarning() << "Qt has trimmed the following forbidden headers from the request:"
+ << trimmedHeaders.join(QLatin1StringView(", "));
+ }
+ customHeaders[i] = nullptr;
+ attr.requestHeaders = customHeaders;
+ }
- if (!extraData.isEmpty()) {
- dataToSend = val(typed_memory_view(extraData.size(),
- reinterpret_cast<const unsigned char *>
- (extraData.constData())));
- }
- m_xhr.set("responseType", val("blob"));
- // set request headers
- for (auto header : request.rawHeaderList()) {
- m_xhr.call<void>("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString());
- }
- m_xhr.call<void>("send", dataToSend);
+ auto url = request.url().toString().toUtf8();
+ QString dPath = "/home/web_user/"_L1 + request.url().fileName();
+ QByteArray destinationPath = dPath.toUtf8();
+ attr.destinationPath = destinationPath.constData();
+ reply->m_fetch = emscripten_fetch(&attr, url.constData());
+ fetchContext->state = FetchContext::State::SENT;
+ });
+ state = Working;
}
void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString)
{
- Q_UNUSED(errorCode)
Q_Q(QNetworkReplyWasmImpl);
q->setError(errorCode, errorString);
- emit q->error(errorCode);
-
- q->setFinished(true);
- emit q->finished();
+ emit q->errorOccurred(errorCode);
}
void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qint64 bytesTotal)
@@ -433,15 +353,16 @@ void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qi
totalDownloadSize = bytesTotal;
- percentFinished = (bytesReceived / bytesTotal) * 100;
+ percentFinished = bytesTotal ? (bytesReceived / bytesTotal) * 100 : 100;
emit q->downloadProgress(bytesReceived, bytesTotal);
}
-void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bufferSize)
+void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer)
{
Q_Q(QNetworkReplyWasmImpl);
+ const qsizetype bufferSize = buffer.size();
if (bufferSize > 0)
q->setReadBufferSize(bufferSize);
@@ -454,15 +375,9 @@ void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bu
totalDownloadSize = downloadBufferCurrentSize;
- downloadBuffer.append(buffer, bufferSize);
+ downloadBuffer.append(buffer);
emit q->readyRead();
-
- if (downloadBufferCurrentSize == totalDownloadSize) {
- q->setFinished(true);
- emit q->readChannelFinished();
- emit q->finished();
- }
}
//taken from qnetworkrequest.cpp
@@ -471,32 +386,36 @@ static int parseHeaderName(const QByteArray &headerName)
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [&](const char *what) {
+ return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (qstricmp(headerName.constData(), "content-type") == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (qstricmp(headerName.constData(), "content-length") == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (qstricmp(headerName.constData(), "cookie") == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
break;
case 'l':
- if (qstricmp(headerName.constData(), "location") == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (qstricmp(headerName.constData(), "server") == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (qstricmp(headerName.constData(), "user-agent") == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -505,25 +424,28 @@ static int parseHeaderName(const QByteArray &headerName)
}
-void QNetworkReplyWasmImplPrivate::headersReceived(const QString &bufferString)
+void QNetworkReplyWasmImplPrivate::headersReceived(const QByteArray &buffer)
{
Q_Q(QNetworkReplyWasmImpl);
- if (!bufferString.isEmpty()) {
- QStringList headers = bufferString.split(QString::fromUtf8("\r\n"), QString::SkipEmptyParts);
+ if (!buffer.isEmpty()) {
+ QList<QByteArray> headers = buffer.split('\n');
for (int i = 0; i < headers.size(); i++) {
- QString headerName = headers.at(i).split(QString::fromUtf8(": ")).at(0);
- QString headersValue = headers.at(i).split(QString::fromUtf8(": ")).at(1);
- if (headerName.isEmpty() || headersValue.isEmpty())
- continue;
+ if (headers.at(i).contains(':')) { // headers include final \x00, so skip
+ QByteArray headerName = headers.at(i).split(':').at(0).trimmed();
+ QByteArray headersValue = headers.at(i).split(':').at(1).trimmed();
- int headerIndex = parseHeaderName(headerName.toLocal8Bit());
+ if (headerName.isEmpty() || headersValue.isEmpty())
+ continue;
- if (headerIndex == -1)
- q->setRawHeader(headerName.toLocal8Bit(), headersValue.toLocal8Bit());
- else
- q->setHeader(static_cast<QNetworkRequest::KnownHeaders>(headerIndex), (QVariant)headersValue);
+ int headerIndex = parseHeaderName(headerName);
+
+ if (headerIndex == -1)
+ q->setRawHeader(headerName, headersValue);
+ else
+ q->setHeader(static_cast<QNetworkRequest::KnownHeaders>(headerIndex), (QVariant)headersValue);
+ }
}
}
emit q->metaDataChanged();
@@ -552,7 +474,7 @@ void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData()
if (!outgoingDataBuffer) {
// first call, create our buffer
- outgoingDataBuffer = QSharedPointer<QRingBuffer>::create();
+ outgoingDataBuffer = std::make_shared<QRingBuffer>();
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
@@ -589,6 +511,110 @@ void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData()
}
}
+void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
+{
+ auto fetchContext = static_cast<FetchContext *>(fetch->userData);
+ std::unique_lock lock{ fetchContext->mutex };
+
+ if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ } else if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::SENT) {
+ const auto reply = fetchContext->reply;
+ if (reply->state != QNetworkReplyPrivate::Aborted) {
+ QByteArray buffer(fetch->data, fetch->numBytes);
+ reply->dataReceived(buffer);
+ QByteArray statusText(fetch->statusText);
+ reply->setStatusCode(fetch->status, statusText);
+ reply->setReplyFinished();
+ }
+ reply->m_fetch = nullptr;
+ fetchContext->state = FetchContext::State::FINISHED;
+ }
+}
+
+void QNetworkReplyWasmImplPrivate::setReplyFinished()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ state = QNetworkReplyPrivate::Finished;
+ q->setFinished(true);
+ emit q->readChannelFinished();
+ emit q->finished();
+}
+
+void QNetworkReplyWasmImplPrivate::setStatusCode(int status, const QByteArray &statusText)
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
+ q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusText);
+}
+
+void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch)
+{
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ const auto reply = fetchContext->reply;
+ if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
+ if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) {
+ size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
+ QByteArray str(headerLength, Qt::Uninitialized);
+ emscripten_fetch_get_response_headers(fetch, str.data(), str.size());
+ reply->headersReceived(str);
+ }
+ }
+}
+
+void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch)
+{
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ const auto reply = fetchContext->reply;
+ if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
+ if (fetch->status < 400) {
+ uint64_t bytes = fetch->dataOffset + fetch->numBytes;
+ uint64_t tBytes = fetch->totalBytes; // totalBytes can be 0 if server not reporting content length
+ if (tBytes == 0)
+ tBytes = bytes;
+ reply->emitDataReadProgress(bytes, tBytes);
+ }
+ }
+}
+
+void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
+{
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ std::unique_lock lock{ fetchContext->mutex };
+
+ if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ } else if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::SENT) {
+ const auto reply = fetchContext->reply;
+ if (reply->state != QNetworkReplyPrivate::Aborted) {
+ QString reasonStr;
+ if (fetch->status > 600)
+ reasonStr = QStringLiteral("Operation canceled");
+ else
+ reasonStr = QString::fromUtf8(fetch->statusText);
+ QByteArray buffer(fetch->data, fetch->numBytes);
+ reply->dataReceived(buffer);
+ QByteArray statusText(fetch->statusText);
+ reply->setStatusCode(fetch->status, statusText);
+ reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()),
+ reasonStr);
+ reply->setReplyFinished();
+ }
+ reply->m_fetch = nullptr;
+ fetchContext->state = FetchContext::State::FINISHED;
+ }
+}
+
//taken from qhttpthreaddelegate.cpp
QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int httpStatusCode, const QUrl &url)
{
@@ -643,6 +669,9 @@ QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int
code = QNetworkReply::ServiceUnavailableError;
break;
+ case 65535: //emscripten reply when aborted
+ code = QNetworkReply::OperationCanceledError;
+ break;
default:
if (httpStatusCode > 500) {
// some kind of server error
@@ -661,3 +690,5 @@ QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreplywasmimpl_p.cpp"
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index e1e6bf4e24..4b00bb09ea 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYWASMIMPL_H
#define QNETWORKREPLYWASMIMPL_H
@@ -61,8 +25,10 @@
#include <private/qabstractfileengine_p.h>
#include <emscripten.h>
-#include <emscripten/html5.h>
-#include <emscripten/val.h>
+#include <emscripten/fetch.h>
+
+#include <memory>
+#include <mutex>
QT_BEGIN_NAMESPACE
@@ -92,12 +58,35 @@ public:
Q_PRIVATE_SLOT(d_func(), void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString))
Q_PRIVATE_SLOT(d_func(), void emitDataReadProgress(qint64 done, qint64 total))
- Q_PRIVATE_SLOT(d_func(), void dataReceived(char *buffer, int bufferSize))
+ Q_PRIVATE_SLOT(d_func(), void dataReceived(const QByteArray &buffer))
private:
QByteArray methodName() const;
};
+class QNetworkReplyWasmImplPrivate;
+
+/*!
+ The FetchContext class ensures the requestData object remains valid
+ while a fetch operation is pending. Since Emscripten fetch is asynchronous,
+ requestData must persist until one of the final callbacks is invoked.
+ Additionally, there's a potential race condition between the thread
+ scheduling the fetch operation and the one executing it. Since fetch must
+ occur on the main thread due to browser limitations,
+ a mutex safeguards the FetchContext to ensure atomic state transitions.
+*/
+struct FetchContext
+{
+ enum class State { SCHEDULED, SENT, FINISHED, CANCELED, TO_BE_DESTROYED };
+
+ FetchContext(QNetworkReplyWasmImplPrivate *networkReply) : reply(networkReply) { }
+
+ QNetworkReplyWasmImplPrivate *reply{ nullptr };
+ std::mutex mutex;
+ QByteArray requestData;
+ State state{ State::SCHEDULED };
+};
+
class QNetworkReplyWasmImplPrivate: public QNetworkReplyPrivate
{
public:
@@ -110,8 +99,10 @@ public:
void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &);
void emitDataReadProgress(qint64 done, qint64 total);
- void dataReceived(const QByteArray &buffer, int bufferSize);
- void headersReceived(const QString &bufferString);
+ void dataReceived(const QByteArray &buffer);
+ void headersReceived(const QByteArray &buffer);
+
+ void setStatusCode(int status, const QByteArray &statusText);
void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
QIODevice *outgoingData);
@@ -120,8 +111,8 @@ public:
void _q_bufferOutgoingData();
void _q_bufferOutgoingDataFinished();
- QSharedPointer<QAtomicInt> pendingDownloadData;
- QSharedPointer<QAtomicInt> pendingDownloadProgress;
+ std::shared_ptr<QAtomicInt> pendingDownloadData;
+ std::shared_ptr<QAtomicInt> pendingDownloadProgress;
qint64 bytesDownloaded;
qint64 bytesBuffered;
@@ -133,12 +124,20 @@ public:
QByteArray downloadBuffer;
QIODevice *outgoingData;
- QSharedPointer<QRingBuffer> outgoingDataBuffer;
+ std::shared_ptr<QRingBuffer> outgoingDataBuffer;
- emscripten::val m_xhr = emscripten::val::null();
- void doAbort() const;
+ static void downloadProgress(emscripten_fetch_t *fetch);
+ static void downloadFailed(emscripten_fetch_t *fetch);
+ static void downloadSucceeded(emscripten_fetch_t *fetch);
+ static void stateChange(emscripten_fetch_t *fetch);
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url);
+
+ emscripten_fetch_t *m_fetch;
+ FetchContext *m_fetchContext;
+ void setReplyFinished();
+ void setCanceled();
+
Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 70b09dba22..0e1172b15c 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -1,54 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkrequest.h"
#include "qnetworkrequest_p.h"
#include "qplatformdefs.h"
#include "qnetworkcookie.h"
#include "qsslconfiguration.h"
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#include "qhttpheadershelper_p.h"
+#if QT_CONFIG(http)
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include "private/http2protocol_p.h"
#endif
-#include "QtCore/qshareddata.h"
-#include "QtCore/qlocale.h"
+
#include "QtCore/qdatetime.h"
+#include "QtCore/qlocale.h"
+#include "QtCore/qshareddata.h"
+#include "QtCore/qtimezone.h"
+#include "QtCore/private/qduplicatetracker_p.h"
+#include "QtCore/private/qtools_p.h"
#include <ctype.h>
#if QT_CONFIG(datestring)
@@ -56,9 +26,18 @@
#endif
#include <algorithm>
+#include <q20algorithm.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+constexpr std::chrono::milliseconds QNetworkRequest::DefaultTransferTimeout;
+
+QT_IMPL_METATYPE_EXTERN(QNetworkRequest)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest__RedirectPolicy)
+
/*!
\class QNetworkRequest
\since 4.4
@@ -133,6 +112,8 @@ QT_BEGIN_NAMESPACE
\value ServerHeader The Server header received by HTTP clients.
+ \omitvalue NumKnownHeaders
+
\sa header(), setHeader(), rawHeader(), setRawHeader()
*/
@@ -168,13 +149,11 @@ QT_BEGIN_NAMESPACE
\value RedirectionTargetAttribute
Replies only, type: QMetaType::QUrl (no default)
If present, it indicates that the server is redirecting the
- request to a different URL. The Network Access API does not by
- default follow redirections: the application can
- determine if the requested redirection should be allowed,
- according to its security policies, or it can set
- QNetworkRequest::FollowRedirectsAttribute to true (in which case
- the redirection will be followed and this attribute will not
- be present in the reply).
+ request to a different URL. The Network Access API does follow
+ redirections by default, unless
+ QNetworkRequest::ManualRedirectPolicy is used. Additionally, if
+ QNetworkRequest::UserVerifiedRedirectPolicy is used, then this
+ attribute will be set if the redirect was not followed.
The returned URL might be relative. Use QUrl::resolved()
to create an absolute URL out of it.
@@ -250,7 +229,7 @@ QT_BEGIN_NAMESPACE
Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
Indicates whether to use cached authorization credentials in the request,
if available. If this is set to QNetworkRequest::Manual and the authentication
- mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
+ mechanism is 'Basic' or 'Digest', Qt will not send an 'Authorization' HTTP
header with any cached credentials it may have for the request's URL.
This attribute is set to QNetworkRequest::Manual by Qt WebKit when creating a cross-origin
XMLHttpRequest where withCredentials has not been set explicitly to true by the
@@ -269,38 +248,19 @@ QT_BEGIN_NAMESPACE
Indicates that this is a background transfer, rather than a user initiated
transfer. Depending on the platform, background transfers may be subject
to different policies.
- The QNetworkSession ConnectInBackground property will be set according to
- this attribute.
-
- \value SpdyAllowedAttribute
- Requests only, type: QMetaType::Bool (default: false)
- Indicates whether the QNetworkAccessManager code is
- allowed to use SPDY with this request. This applies only
- to SSL requests, and depends on the server supporting SPDY.
- Obsolete, use Http2 instead of Spdy.
-
- \value SpdyWasUsedAttribute
- Replies only, type: QMetaType::Bool
- Indicates whether SPDY was used for receiving
- this reply. Obsolete, use Http2 instead of Spdy.
\value Http2AllowedAttribute
- Requests only, type: QMetaType::Bool (default: false)
+ Requests only, type: QMetaType::Bool (default: true)
Indicates whether the QNetworkAccessManager code is
allowed to use HTTP/2 with this request. This applies
- to SSL requests or 'cleartext' HTTP/2.
+ to SSL requests or 'cleartext' HTTP/2 if Http2CleartextAllowedAttribute
+ is set.
\value Http2WasUsedAttribute
Replies only, type: QMetaType::Bool (default: false)
Indicates whether HTTP/2 was used for receiving this reply.
(This value was introduced in 5.9.)
- \value HTTP2AllowedAttribute
- Obsolete alias for Http2AllowedAttribute.
-
- \value HTTP2WasUsedAttribute
- Obsolete alias for Http2WasUsedAttribute.
-
\value EmitAllUploadProgressSignalsAttribute
Requests only, type: QMetaType::Bool (default: false)
Indicates whether all upload signals should be emitted.
@@ -308,13 +268,6 @@ QT_BEGIN_NAMESPACE
in 100 millisecond intervals.
(This value was introduced in 5.5.)
- \value FollowRedirectsAttribute
- Requests only, type: QMetaType::Bool (default: false)
- Indicates whether the Network Access API should automatically follow a
- HTTP redirect response or not. Currently redirects that are insecure,
- that is redirecting from "https" to "http" protocol, are not allowed.
- (This value was introduced in 5.6.)
-
\value OriginalContentLengthAttribute
Replies only, type QMetaType::Int
Holds the original content-length attribute before being invalidated and
@@ -324,8 +277,8 @@ QT_BEGIN_NAMESPACE
\value RedirectPolicyAttribute
Requests only, type: QMetaType::Int, should be one of the
- QNetworkRequest::RedirectPolicy values (default: ManualRedirectPolicy).
- This attribute obsoletes FollowRedirectsAttribute.
+ QNetworkRequest::RedirectPolicy values
+ (default: NoLessSafeRedirectPolicy).
(This value was introduced in 5.9.)
\value Http2DirectAttribute
@@ -333,8 +286,9 @@ QT_BEGIN_NAMESPACE
If set, this attribute will force QNetworkAccessManager to use
HTTP/2 protocol without initial HTTP/2 protocol negotiation.
Use of this attribute implies prior knowledge that a particular
- server supports HTTP/2. The attribute works with SSL or 'cleartext'
- HTTP/2. If a server turns out to not support HTTP/2, when HTTP/2 direct
+ server supports HTTP/2. The attribute works with SSL or with 'cleartext'
+ HTTP/2 if Http2CleartextAllowedAttribute is set.
+ If a server turns out to not support HTTP/2, when HTTP/2 direct
was specified, QNetworkAccessManager gives up, without attempting to
fall back to HTTP/1.1. If both Http2AllowedAttribute and
Http2DirectAttribute are set, Http2DirectAttribute takes priority.
@@ -348,6 +302,38 @@ QT_BEGIN_NAMESPACE
the QNetworkReply after having emitted "finished".
(This value was introduced in 5.14.)
+ \value ConnectionCacheExpiryTimeoutSecondsAttribute
+ Requests only, type: QMetaType::Int
+ To set when the TCP connections to a server (HTTP1 and HTTP2) should
+ be closed after the last pending request had been processed.
+ (This value was introduced in 6.3.)
+
+ \value Http2CleartextAllowedAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ If set, this attribute will tell QNetworkAccessManager to attempt
+ an upgrade to HTTP/2 over cleartext (also known as h2c).
+ Until Qt 7 the default value for this attribute can be overridden
+ to true by setting the QT_NETWORK_H2C_ALLOWED environment variable.
+ This attribute is ignored if the Http2AllowedAttribute is not set.
+ (This value was introduced in 6.3.)
+
+ \value UseCredentialsAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates if the underlying XMLHttpRequest cross-site Access-Control
+ requests should be made using credentials. Has no effect on
+ same-origin requests. This only affects the WebAssembly platform.
+ (This value was introduced in 6.5.)
+
+ \value FullLocalServerNameAttribute
+ Requests only, type: QMetaType::String
+ Holds the full local server name to be used for the underlying
+ QLocalSocket. This attribute is used by the QNetworkAccessManager
+ to connect to a specific local server, when QLocalSocket's behavior for
+ a simple name isn't enough. The URL in the QNetworkRequest must still
+ use unix+http: or local+http: scheme. And the hostname in the URL will
+ be used for the Host header in the HTTP request.
+ (This value was introduced in 6.8.)
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -402,12 +388,11 @@ QT_BEGIN_NAMESPACE
Indicates whether the Network Access API should automatically follow a
HTTP redirect response or not.
- \value ManualRedirectPolicy Default value: not following any redirects.
+ \value ManualRedirectPolicy Not following any redirects.
- \value NoLessSafeRedirectPolicy Only "http"->"http", "http" -> "https"
- or "https" -> "https" redirects are allowed.
- Equivalent to setting the old FollowRedirectsAttribute
- to true
+ \value NoLessSafeRedirectPolicy Default value: Only "http"->"http",
+ "http" -> "https" or "https" -> "https" redirects
+ are allowed.
\value SameOriginRedirectPolicy Require the same protocol, host and port.
Note, http://example.com and http://example.com:80
@@ -423,6 +408,11 @@ QT_BEGIN_NAMESPACE
for example, to ask the user whether to
accept the redirect, or to decide
based on some app-specific configuration.
+
+ \note When Qt handles redirects it will, for legacy and compatibility
+ reasons, issue the redirected request using GET when the server returns
+ a 301 or 302 response, regardless of the original method used, unless it was
+ HEAD.
*/
/*!
@@ -432,9 +422,19 @@ QT_BEGIN_NAMESPACE
A constant that can be used for enabling transfer
timeouts with a preset value.
- \value TransferTimeoutPreset The transfer timeout in milliseconds.
- Used if setTimeout() is called
- without an argument.
+ \value DefaultTransferTimeoutConstant The transfer timeout in milliseconds.
+ Used if setTimeout() is called
+ without an argument.
+
+ \sa QNetworkRequest::DefaultTransferTimeout
+ */
+
+/*!
+ \variable QNetworkRequest::DefaultTransferTimeout
+
+ The transfer timeout with \l {QNetworkRequest::TransferTimeoutConstant}
+ milliseconds. Used if setTransferTimeout() is called without an
+ argument.
*/
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
@@ -447,7 +447,6 @@ public:
, sslConfiguration(nullptr)
#endif
, maxRedirectsAllowed(maxRedirectCount)
- , transferTimeout(0)
{ qRegisterMetaType<QNetworkRequest>(); }
~QNetworkRequestPrivate()
{
@@ -470,7 +469,9 @@ public:
#endif
peerVerifyName = other.peerVerifyName;
#if QT_CONFIG(http)
+ h1Configuration = other.h1Configuration;
h2Configuration = other.h2Configuration;
+ decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
#endif
transferTimeout = other.transferTimeout;
}
@@ -479,14 +480,16 @@ public:
{
return url == other.url &&
priority == other.priority &&
- rawHeaders == other.rawHeaders &&
attributes == other.attributes &&
maxRedirectsAllowed == other.maxRedirectsAllowed &&
peerVerifyName == other.peerVerifyName
#if QT_CONFIG(http)
+ && h1Configuration == other.h1Configuration
&& h2Configuration == other.h2Configuration
+ && decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
#endif
&& transferTimeout == other.transferTimeout
+ && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
;
// don't compare cookedHeaders
}
@@ -499,9 +502,11 @@ public:
int maxRedirectsAllowed;
QString peerVerifyName;
#if QT_CONFIG(http)
+ QHttp1Configuration h1Configuration;
QHttp2Configuration h2Configuration;
+ qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
#endif
- int transferTimeout;
+ std::chrono::milliseconds transferTimeout = 0ms;
};
/*!
@@ -514,10 +519,11 @@ QNetworkRequest::QNetworkRequest()
: d(new QNetworkRequestPrivate)
{
#if QT_CONFIG(http)
- // Initial values proposed by RFC 7540 are quite draconian,
- // so unless an application will set its own parameters, we
- // make stream window size larger and increase (via WINDOW_UPDATE)
- // the session window size. These are our 'defaults':
+ // Initial values proposed by RFC 7540 are quite draconian, but we
+ // know about servers configured with this value as maximum possible,
+ // rejecting our SETTINGS frame and sending us a GOAWAY frame with the
+ // flow control error set. If this causes a problem - the app should
+ // set a proper configuration. We'll use our defaults, as documented.
d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
d->h2Configuration.setServerPushEnabled(false);
@@ -610,6 +616,43 @@ void QNetworkRequest::setUrl(const QUrl &url)
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this network request.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkRequest::headers() const
+{
+ return d->headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network request, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, the values will
+ be parsed and the corresponding parsed form will also be set.
+
+ \sa headers(), KnownHeaders
+*/
+void QNetworkRequest::setHeaders(QHttpHeaders &&newHeaders)
+{
+ d->setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkRequest::setHeaders(const QHttpHeaders &newHeaders)
+{
+ d->setHeaders(newHeaders);
+}
+
+/*!
Returns the value of the known network header \a header if it is
present in this request. If it is not present, returns QVariant()
(i.e., an invalid variant).
@@ -638,10 +681,11 @@ void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
network request.
\sa rawHeader(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
{
- return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+ return d->headers().contains(headerName);
}
/*!
@@ -653,14 +697,11 @@ bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
Raw headers can be set with setRawHeader() or with setHeader().
\sa header(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
{
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->rawHeader(headerName);
}
/*!
@@ -745,9 +786,8 @@ QSslConfiguration QNetworkRequest::sslConfiguration() const
/*!
Sets this network request's SSL configuration to be \a config. The
settings that apply are the private key, the local certificate,
- the SSL protocol (SSLv2, SSLv3, TLSv1.0 where applicable), the CA
- certificates and the ciphers that the SSL backend is allowed to
- use.
+ the TLS protocol (e.g. TLS 1.3), the CA certificates and the ciphers that
+ the SSL backend is allowed to use.
\sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
*/
@@ -881,7 +921,31 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+/*!
+ \since 6.5
+
+ Returns the current parameters that QNetworkAccessManager is
+ using for the underlying HTTP/1 connection of this request.
+
+ \sa setHttp1Configuration
+*/
+QHttp1Configuration QNetworkRequest::http1Configuration() const
+{
+ return d->h1Configuration;
+}
+/*!
+ \since 6.5
+
+ Sets request's HTTP/1 parameters from \a configuration.
+
+ \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
+*/
+void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
+{
+ d->h1Configuration = configuration;
+}
+
/*!
\since 5.14
@@ -894,7 +958,7 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
\list
\li Window size for connection-level flowcontrol is 2147483647 octets
- \li Window size for stream-level flowcontrol is 21474836 octets
+ \li Window size for stream-level flowcontrol is 214748364 octets
\li Max frame size is 16384
\endlist
@@ -927,87 +991,184 @@ void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configura
}
/*!
+ \since 6.2
+
+ Returns the threshold for archive bomb checks.
+
+ If the decompressed size of a reply is smaller than this, Qt will simply
+ decompress it, without further checking.
+
+ \sa setDecompressedSafetyCheckThreshold()
+*/
+qint64 QNetworkRequest::decompressedSafetyCheckThreshold() const
+{
+ return d->decompressedSafetyCheckThreshold;
+}
+
+/*!
+ \since 6.2
+
+ Sets the \a threshold for archive bomb checks.
+
+ Some supported compression algorithms can, in a tiny compressed file, encode
+ a spectacularly huge decompressed file. This is only possible if the
+ decompressed content is extremely monotonous, which is seldom the case for
+ real files being transmitted in good faith: files exercising such insanely
+ high compression ratios are typically payloads of buffer-overrun attacks, or
+ denial-of-service (by using up too much memory) attacks. Consequently, files
+ that decompress to huge sizes, particularly from tiny compressed forms, are
+ best rejected as suspected malware.
+
+ If a reply's decompressed size is bigger than this threshold (by default,
+ 10 MiB, i.e. 10 * 1024 * 1024), Qt will check the compression ratio: if that
+ is unreasonably large (40:1 for GZip and Deflate, or 100:1 for Brotli and
+ ZStandard), the reply will be treated as an error. Setting the threshold
+ to \c{-1} disables this check.
+
+ \sa decompressedSafetyCheckThreshold()
+*/
+void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
+{
+ d->decompressedSafetyCheckThreshold = threshold;
+}
+#endif // QT_CONFIG(http)
+
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+/*!
+ \fn int QNetworkRequest::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
+*/
+
+/*!
+ \fn void QNetworkRequest::setTransferTimeout(int timeout)
+ \since 5.15
- \sa setTransferTimeout
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
*/
-int QNetworkRequest::transferTimeout() const
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
+*/
+std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const
{
return d->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::TransferTimeoutPreset. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero.
- \sa transferTimeout
+ \sa transferTimeoutAsDuration()
*/
-void QNetworkRequest::setTransferTimeout(int timeout)
+void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
{
- d->transferTimeout = timeout;
+ d->transferTimeout = duration;
}
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
-static QByteArray headerName(QNetworkRequest::KnownHeaders header)
-{
- switch (header) {
- case QNetworkRequest::ContentTypeHeader:
- return "Content-Type";
-
- case QNetworkRequest::ContentLengthHeader:
- return "Content-Length";
-
- case QNetworkRequest::LocationHeader:
- return "Location";
-
- case QNetworkRequest::LastModifiedHeader:
- return "Last-Modified";
-
- case QNetworkRequest::IfModifiedSinceHeader:
- return "If-Modified-Since";
+namespace {
- case QNetworkRequest::ETagHeader:
- return "ETag";
+struct HeaderPair {
+ QHttpHeaders::WellKnownHeader wellKnownHeader;
+ QNetworkRequest::KnownHeaders knownHeader;
+};
- case QNetworkRequest::IfMatchHeader:
- return "If-Match";
+constexpr bool operator<(const HeaderPair &lhs, const HeaderPair &rhs)
+{
+ return lhs.wellKnownHeader < rhs.wellKnownHeader;
+}
- case QNetworkRequest::IfNoneMatchHeader:
- return "If-None-Match";
+constexpr bool operator<(const HeaderPair &lhs, QHttpHeaders::WellKnownHeader rhs)
+{
+ return lhs.wellKnownHeader < rhs;
+}
- case QNetworkRequest::CookieHeader:
- return "Cookie";
+constexpr bool operator<(QHttpHeaders::WellKnownHeader lhs, const HeaderPair &rhs)
+{
+ return lhs < rhs.wellKnownHeader;
+}
- case QNetworkRequest::SetCookieHeader:
- return "Set-Cookie";
+} // anonymous namespace
+
+static constexpr HeaderPair knownHeadersArr[] = {
+ { QHttpHeaders::WellKnownHeader::ContentDisposition, QNetworkRequest::KnownHeaders::ContentDispositionHeader },
+ { QHttpHeaders::WellKnownHeader::ContentLength, QNetworkRequest::KnownHeaders::ContentLengthHeader },
+ { QHttpHeaders::WellKnownHeader::ContentType, QNetworkRequest::KnownHeaders::ContentTypeHeader },
+ { QHttpHeaders::WellKnownHeader::Cookie, QNetworkRequest::KnownHeaders::CookieHeader },
+ { QHttpHeaders::WellKnownHeader::ETag, QNetworkRequest::KnownHeaders::ETagHeader },
+ { QHttpHeaders::WellKnownHeader::IfMatch , QNetworkRequest::KnownHeaders::IfMatchHeader },
+ { QHttpHeaders::WellKnownHeader::IfModifiedSince, QNetworkRequest::KnownHeaders::IfModifiedSinceHeader },
+ { QHttpHeaders::WellKnownHeader::IfNoneMatch, QNetworkRequest::KnownHeaders::IfNoneMatchHeader },
+ { QHttpHeaders::WellKnownHeader::LastModified, QNetworkRequest::KnownHeaders::LastModifiedHeader},
+ { QHttpHeaders::WellKnownHeader::Location, QNetworkRequest::KnownHeaders::LocationHeader},
+ { QHttpHeaders::WellKnownHeader::Server, QNetworkRequest::KnownHeaders::ServerHeader },
+ { QHttpHeaders::WellKnownHeader::SetCookie, QNetworkRequest::KnownHeaders::SetCookieHeader },
+ { QHttpHeaders::WellKnownHeader::UserAgent, QNetworkRequest::KnownHeaders::UserAgentHeader }
+};
- case QNetworkRequest::ContentDispositionHeader:
- return "Content-Disposition";
+static_assert(std::size(knownHeadersArr) == size_t(QNetworkRequest::KnownHeaders::NumKnownHeaders));
+static_assert(q20::is_sorted(std::begin(knownHeadersArr), std::end(knownHeadersArr)));
- case QNetworkRequest::UserAgentHeader:
- return "User-Agent";
+static std::optional<QNetworkRequest::KnownHeaders> toKnownHeader(QHttpHeaders::WellKnownHeader key)
+{
+ const auto it = std::lower_bound(std::begin(knownHeadersArr), std::end(knownHeadersArr), key);
+ if (it == std::end(knownHeadersArr) || key < *it)
+ return std::nullopt;
+ return it->knownHeader;
+}
- case QNetworkRequest::ServerHeader:
- return "Server";
+static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QNetworkRequest::KnownHeaders key)
+{
+ auto pred = [key](const HeaderPair &pair) { return pair.knownHeader == key; };
+ const auto it = std::find_if(std::begin(knownHeadersArr), std::end(knownHeadersArr), pred);
+ if (it == std::end(knownHeadersArr))
+ return std::nullopt;
+ return it->wellKnownHeader;
+}
- // no default:
- // if new values are added, this will generate a compiler warning
+static QByteArray makeCookieHeader(const QList<QNetworkCookie> &cookies,
+ QNetworkCookie::RawForm type,
+ QByteArrayView separator)
+{
+ QByteArray result;
+ for (const QNetworkCookie &cookie : cookies) {
+ result += cookie.toRawForm(type);
+ result += separator;
}
+ if (!result.isEmpty())
+ result.chop(separator.size());
+ return result;
+}
- return QByteArray();
+static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type,
+ QByteArrayView separator)
+{
+ const QList<QNetworkCookie> *cookies = get_if<QList<QNetworkCookie>>(&value);
+ if (!cookies)
+ return {};
+ return makeCookieHeader(*cookies, type, separator);
}
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
@@ -1035,98 +1196,78 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
case QNetworkRequest::LastModifiedHeader:
case QNetworkRequest::IfModifiedSinceHeader:
switch (value.userType()) {
+ // Generate RFC 1123/822 dates:
case QMetaType::QDate:
+ return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(QTimeZone::UTC));
case QMetaType::QDateTime:
- // generate RFC 1123/822 dates:
return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
default:
return value.toByteArray();
}
- case QNetworkRequest::CookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += "; ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
- }
- return result;
- }
+ case QNetworkRequest::CookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::NameAndValueOnly, "; ");
- case QNetworkRequest::SetCookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += ", ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::Full);
- }
- return result;
- }
- }
+ case QNetworkRequest::SetCookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::Full, ", ");
- return QByteArray();
+ default:
+ Q_UNREACHABLE_RETURN({});
+ }
}
-static int parseHeaderName(const QByteArray &headerName)
+static int parseHeaderName(QByteArrayView headerName)
{
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [headerName](QByteArrayView what) {
+ return headerName.compare(what, Qt::CaseInsensitive) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (headerName.compare("content-type", Qt::CaseInsensitive) == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
- else if (qstricmp(headerName.constData(), "content-disposition") == 0)
+ else if (is("content-disposition"))
return QNetworkRequest::ContentDispositionHeader;
break;
case 'e':
- if (qstricmp(headerName.constData(), "etag") == 0)
+ if (is("etag"))
return QNetworkRequest::ETagHeader;
break;
case 'i':
- if (qstricmp(headerName.constData(), "if-modified-since") == 0)
+ if (is("if-modified-since"))
return QNetworkRequest::IfModifiedSinceHeader;
- if (qstricmp(headerName.constData(), "if-match") == 0)
+ if (is("if-match"))
return QNetworkRequest::IfMatchHeader;
- if (qstricmp(headerName.constData(), "if-none-match") == 0)
+ if (is("if-none-match"))
return QNetworkRequest::IfNoneMatchHeader;
break;
case 'l':
- if (headerName.compare("location", Qt::CaseInsensitive) == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (headerName.compare("server", Qt::CaseInsensitive) == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -1134,7 +1275,7 @@ static int parseHeaderName(const QByteArray &headerName)
return -1; // nothing found
}
-static QVariant parseHttpDate(const QByteArray &raw)
+static QVariant parseHttpDate(QByteArrayView raw)
{
QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
if (dt.isValid())
@@ -1142,24 +1283,23 @@ static QVariant parseHttpDate(const QByteArray &raw)
return QVariant(); // transform an invalid QDateTime into a null QVariant
}
-static QVariant parseCookieHeader(const QByteArray &raw)
+static QList<QNetworkCookie> parseCookieHeader(QByteArrayView raw)
{
QList<QNetworkCookie> result;
- const QList<QByteArray> cookieList = raw.split(';');
- for (const QByteArray &cookie : cookieList) {
+ for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
- if (parsed.count() != 1)
- return QVariant(); // invalid Cookie: header
+ if (parsed.size() != 1)
+ return {}; // invalid Cookie: header
result += parsed;
}
- return QVariant::fromValue(result);
+ return result;
}
-static QVariant parseETag(const QByteArray &raw)
+static QVariant parseETag(QByteArrayView raw)
{
- const QByteArray trimmed = raw.trimmed();
+ const QByteArrayView trimmed = raw.trimmed();
if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
return QVariant();
@@ -1169,50 +1309,38 @@ static QVariant parseETag(const QByteArray &raw)
return QString::fromLatin1(trimmed);
}
-static QVariant parseIfMatch(const QByteArray &raw)
+template<typename T>
+static QStringList parseMatchImpl(QByteArrayView raw, T op)
{
- const QByteArray trimmedRaw = raw.trimmed();
+ const QByteArrayView trimmedRaw = raw.trimmed();
if (trimmedRaw == "*")
return QStringList(QStringLiteral("*"));
QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"'))
- continue;
-
- if (!trimmed.endsWith('"'))
- continue;
-
- tags += QString::fromLatin1(trimmed);
+ for (auto &element : QLatin1StringView(trimmedRaw).tokenize(','_L1)) {
+ if (const auto trimmed = element.trimmed(); op(trimmed))
+ tags += QString::fromLatin1(trimmed);
}
return tags;
}
-static QVariant parseIfNoneMatch(const QByteArray &raw)
-{
- const QByteArray trimmedRaw = raw.trimmed();
- if (trimmedRaw == "*")
- return QStringList(QStringLiteral("*"));
-
- QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
- continue;
- if (!trimmed.endsWith('"'))
- continue;
+static QStringList parseIfMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return element.startsWith('"') && element.endsWith('"');
+ });
+}
- tags += QString::fromLatin1(trimmed);
- }
- return tags;
+static QStringList parseIfNoneMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
+ });
}
-static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
+static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QByteArrayView value)
{
// header is always a valid value
switch (header) {
@@ -1225,7 +1353,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
case QNetworkRequest::ContentLengthHeader: {
bool ok;
- qint64 result = value.trimmed().toLongLong(&ok);
+ qint64 result = QByteArrayView(value).trimmed().toLongLong(&ok);
if (ok)
return result;
return QVariant();
@@ -1252,136 +1380,231 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
return parseIfNoneMatch(value);
case QNetworkRequest::CookieHeader:
- return parseCookieHeader(value);
+ return QVariant::fromValue(parseCookieHeader(value));
case QNetworkRequest::SetCookieHeader:
return QVariant::fromValue(QNetworkCookie::parseCookies(value));
default:
- Q_ASSERT(0);
+ Q_UNREACHABLE_RETURN({});
+ }
+}
+
+static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QList<QByteArray> values)
+{
+ if (values.empty())
+ return QVariant();
+
+ // header is always a valid value
+ switch (header) {
+ case QNetworkRequest::IfMatchHeader: {
+ QStringList res;
+ for (const auto &val : values)
+ res << parseIfMatch(val);
+ return res;
+ }
+ case QNetworkRequest::IfNoneMatchHeader: {
+ QStringList res;
+ for (const auto &val : values)
+ res << parseIfNoneMatch(val);
+ return res;
+ }
+ case QNetworkRequest::CookieHeader: {
+ auto listOpt = QNetworkHeadersPrivate::toCookieList(values);
+ return listOpt.has_value() ? QVariant::fromValue(listOpt.value()) : QVariant();
+ }
+ case QNetworkRequest::SetCookieHeader: {
+ QList<QNetworkCookie> res;
+ for (const auto &val : values)
+ res << QNetworkCookie::parseCookies(val);
+ return QVariant::fromValue(res);
+ }
+ default:
+ return parseHeaderValue(header, values.first());
}
return QVariant();
}
-QNetworkHeadersPrivate::RawHeadersList::ConstIterator
-QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
+static bool isSetCookie(QByteArrayView name)
{
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- if (it->first.compare(key, Qt::CaseInsensitive) == 0)
- return it;
+ return name.compare(QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::SetCookie),
+ Qt::CaseInsensitive) == 0;
+}
+
+static bool isSetCookie(QHttpHeaders::WellKnownHeader name)
+{
+ return name == QHttpHeaders::WellKnownHeader::SetCookie;
+}
+
+template<class HeaderName>
+static void setFromRawHeader(QHttpHeaders &headers, HeaderName header,
+ QByteArrayView value)
+{
+ headers.removeAll(header);
+
+ if (value.isNull())
+ // only wanted to erase key
+ return;
- return end; // not found
+ if (isSetCookie(header)) {
+ for (auto cookie : QLatin1StringView(value).tokenize('\n'_L1))
+ headers.append(QHttpHeaders::WellKnownHeader::SetCookie, cookie);
+ } else {
+ headers.append(header, value);
+ }
}
-QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
+const QNetworkHeadersPrivate::RawHeadersList &QNetworkHeadersPrivate::allRawHeaders() const
{
- return rawHeaders;
+ if (rawHeaderCache.isCached)
+ return rawHeaderCache.headersList;
+
+ rawHeaderCache.headersList = fromHttpToRaw(httpHeaders);
+ rawHeaderCache.isCached = true;
+ return rawHeaderCache.headersList;
}
QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
+ if (httpHeaders.isEmpty())
+ return {};
+
QList<QByteArray> result;
- result.reserve(rawHeaders.size());
- RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- result << it->first;
+ result.reserve(httpHeaders.size());
+ QDuplicateTracker<QByteArray> seen(httpHeaders.size());
+
+ for (qsizetype i = 0; i < httpHeaders.size(); i++) {
+ const auto nameL1 = httpHeaders.nameAt(i);
+ const auto name = QByteArray(nameL1.data(), nameL1.size());
+ if (seen.hasSeen(name))
+ continue;
+
+ result << name;
+ }
return result;
}
+QByteArray QNetworkHeadersPrivate::rawHeader(QAnyStringView headerName) const
+{
+ QByteArrayView setCookieStr = QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie);
+ if (QAnyStringView::compare(headerName, setCookieStr, Qt::CaseInsensitive) != 0)
+ return httpHeaders.combinedValue(headerName);
+
+ QByteArray result;
+ const char* separator = "";
+ for (qsizetype i = 0; i < httpHeaders.size(); ++i) {
+ if (QAnyStringView::compare(httpHeaders.nameAt(i), headerName, Qt::CaseInsensitive) == 0) {
+ result.append(separator);
+ result.append(httpHeaders.valueAt(i));
+ separator = "\n";
+ }
+ }
+ return result;
+}
+
void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
{
if (key.isEmpty())
// refuse to accept an empty raw header
return;
- setRawHeaderInternal(key, value);
+ setFromRawHeader(httpHeaders, key, value);
parseAndSetHeader(key, value);
-}
-
-/*!
- \internal
- Sets the internal raw headers list to match \a list. The cooked headers
- will also be updated.
- If \a list contains duplicates, they will be stored, but only the first one
- is usually accessed.
-*/
-void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
-{
- cookedHeaders.clear();
- rawHeaders = list;
-
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- parseAndSetHeader(it->first, it->second);
+ invalidateHeaderCache();
}
void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
const QVariant &value)
{
- QByteArray name = headerName(header);
- if (name.isEmpty()) {
- // headerName verifies that \a header is a known value
+ const auto wellKnownOpt = toWellKnownHeader(header);
+ if (!wellKnownOpt) {
+ // verifies that \a header is a known value
qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
return;
}
if (value.isNull()) {
- setRawHeaderInternal(name, QByteArray());
+ httpHeaders.removeAll(wellKnownOpt.value());
cookedHeaders.remove(header);
} else {
QByteArray rawValue = headerValue(header, value);
if (rawValue.isEmpty()) {
qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
- value.typeName(), name.constData());
+ value.typeName(),
+ QHttpHeaders::wellKnownHeaderName(wellKnownOpt.value()).constData());
return;
}
- setRawHeaderInternal(name, rawValue);
+ setFromRawHeader(httpHeaders, wellKnownOpt.value(), rawValue);
cookedHeaders.insert(header, value);
}
+
+ invalidateHeaderCache();
}
-void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
+QHttpHeaders QNetworkHeadersPrivate::headers() const
{
- auto firstEqualsKey = [&key](const RawHeaderPair &header) {
- return header.first.compare(key, Qt::CaseInsensitive) == 0;
- };
- rawHeaders.erase(std::remove_if(rawHeaders.begin(), rawHeaders.end(),
- firstEqualsKey),
- rawHeaders.end());
+ return httpHeaders;
+}
- if (value.isNull())
- return; // only wanted to erase key
+void QNetworkHeadersPrivate::setHeaders(const QHttpHeaders &newHeaders)
+{
+ httpHeaders = newHeaders;
+ setCookedFromHttp(httpHeaders);
+ invalidateHeaderCache();
+}
+
+void QNetworkHeadersPrivate::setHeaders(QHttpHeaders &&newHeaders)
+{
+ httpHeaders = std::move(newHeaders);
+ setCookedFromHttp(httpHeaders);
+ invalidateHeaderCache();
+}
+
+void QNetworkHeadersPrivate::setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value)
+{
+ httpHeaders.replaceOrAppend(name, value);
+
+ // set cooked header
+ const auto knownHeaderOpt = toKnownHeader(name);
+ if (knownHeaderOpt)
+ parseAndSetHeader(knownHeaderOpt.value(), value);
+
+ invalidateHeaderCache();
+}
- RawHeaderPair pair;
- pair.first = key;
- pair.second = value;
- rawHeaders.append(pair);
+void QNetworkHeadersPrivate::clearHeaders()
+{
+ httpHeaders.clear();
+ cookedHeaders.clear();
+ invalidateHeaderCache();
}
-void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
+void QNetworkHeadersPrivate::parseAndSetHeader(QByteArrayView key, QByteArrayView value)
{
// is it a known header?
const int parsedKeyAsInt = parseHeaderName(key);
if (parsedKeyAsInt != -1) {
const QNetworkRequest::KnownHeaders parsedKey
= static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
- if (value.isNull()) {
- cookedHeaders.remove(parsedKey);
- } else if (parsedKey == QNetworkRequest::ContentLengthHeader
- && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
- // Only set the cooked header "Content-Length" once.
- // See bug QTBUG-15311
- } else {
- cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
- }
+ parseAndSetHeader(parsedKey, value);
+ }
+}
+void QNetworkHeadersPrivate::parseAndSetHeader(QNetworkRequest::KnownHeaders key,
+ QByteArrayView value)
+{
+ if (value.isNull()) {
+ cookedHeaders.remove(key);
+ } else if (key == QNetworkRequest::ContentLengthHeader
+ && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
+ // Only set the cooked header "Content-Length" once.
+ // See bug QTBUG-15311
+ } else {
+ cookedHeaders.insert(key, parseHeaderValue(key, value));
}
}
@@ -1435,7 +1658,7 @@ static int name_to_month(const char* month_str)
return 0;
}
-QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
+QDateTime QNetworkHeadersPrivate::fromHttpDate(QByteArrayView value)
{
// HTTP dates have three possible formats:
// RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
@@ -1469,20 +1692,152 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
// eat the weekday, the comma and the space following it
QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
// must be RFC 850 date
- dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
+ dt = c.toDateTime(sansWeekday, "dd-MMM-yy hh:mm:ss 'GMT'"_L1);
}
}
#endif // datestring
if (dt.isValid())
- dt.setTimeSpec(Qt::UTC);
+ dt.setTimeZone(QTimeZone::UTC);
return dt;
}
QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
{
- return QLocale::c().toString(dt, u"ddd, dd MMM yyyy hh:mm:ss 'GMT'")
- .toLatin1();
+ return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
+}
+
+QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::fromHttpToRaw(
+ const QHttpHeaders &headers)
+{
+ if (headers.isEmpty())
+ return {};
+
+ QNetworkHeadersPrivate::RawHeadersList list;
+ QHash<QByteArray, qsizetype> nameToIndex;
+ list.reserve(headers.size());
+ nameToIndex.reserve(headers.size());
+
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ const auto nameL1 = headers.nameAt(i);
+ const auto value = headers.valueAt(i);
+
+ const bool isSetCookie = nameL1 == QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie);
+
+ const auto name = QByteArray(nameL1.data(), nameL1.size());
+ if (auto it = nameToIndex.find(name); it != nameToIndex.end()) {
+ list[it.value()].second += isSetCookie ? "\n" : ", ";
+ list[it.value()].second += value;
+ } else {
+ nameToIndex[name] = list.size();
+ list.emplaceBack(name, value.toByteArray());
+ }
+ }
+
+ return list;
+}
+
+QHttpHeaders QNetworkHeadersPrivate::fromRawToHttp(const RawHeadersList &raw)
+{
+ if (raw.empty())
+ return {};
+
+ QHttpHeaders headers;
+ headers.reserve(raw.size());
+
+ for (const auto &[key, value] : raw) {
+ const bool isSetCookie = key.compare(QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie),
+ Qt::CaseInsensitive) == 0;
+ if (isSetCookie) {
+ for (auto header : QLatin1StringView(value).tokenize('\n'_L1))
+ headers.append(key, header);
+ } else {
+ headers.append(key, value);
+ }
+ }
+
+ return headers;
+}
+
+std::optional<qint64> QNetworkHeadersPrivate::toInt(QByteArrayView value)
+{
+ if (value.empty())
+ return std::nullopt;
+
+ bool ok;
+ qint64 res = value.toLongLong(&ok);
+ if (ok)
+ return res;
+ return std::nullopt;
+}
+
+std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toSetCookieList(
+ const QList<QByteArray> &values)
+{
+ if (values.empty())
+ return std::nullopt;
+
+ QList<QNetworkCookie> cookies;
+ for (const auto &s : values)
+ cookies += QNetworkCookie::parseCookies(s);
+
+ if (cookies.empty())
+ return std::nullopt;
+ return cookies;
+}
+
+QByteArray QNetworkHeadersPrivate::fromCookieList(const QList<QNetworkCookie> &cookies)
+{
+ return makeCookieHeader(cookies, QNetworkCookie::NameAndValueOnly, "; ");
+}
+
+std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toCookieList(
+ const QList<QByteArray> &values)
+{
+ if (values.empty())
+ return std::nullopt;
+
+ QList<QNetworkCookie> cookies;
+ for (const auto &s : values)
+ cookies += parseCookieHeader(s);
+
+ if (cookies.empty())
+ return std::nullopt;
+ return cookies;
+}
+
+void QNetworkHeadersPrivate::invalidateHeaderCache()
+{
+ rawHeaderCache.headersList.clear();
+ rawHeaderCache.isCached = false;
+}
+
+void QNetworkHeadersPrivate::setCookedFromHttp(const QHttpHeaders &newHeaders)
+{
+ cookedHeaders.clear();
+
+ QMap<QNetworkRequest::KnownHeaders, QList<QByteArray>> multipleHeadersMap;
+ for (int i = 0; i < newHeaders.size(); ++i) {
+ const auto name = newHeaders.nameAt(i);
+ const auto value = newHeaders.valueAt(i);
+
+ const int parsedKeyAsInt = parseHeaderName(name);
+ if (parsedKeyAsInt == -1)
+ continue;
+
+ const QNetworkRequest::KnownHeaders parsedKey
+ = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
+
+ auto &list = multipleHeadersMap[parsedKey];
+ list.append(value.toByteArray());
+ }
+
+ for (auto i = multipleHeadersMap.cbegin(), end = multipleHeadersMap.cend(); i != end; ++i)
+ cookedHeaders.insert(i.key(), parseHeaderValue(i.key(), i.value()));
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkrequest.cpp"
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index dcd2c6b61f..368eb99d95 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREQUEST_H
#define QNETWORKREQUEST_H
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtNetwork/qhttpheaders.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QString>
#include <QtCore/QUrl>
@@ -50,10 +15,12 @@ QT_BEGIN_NAMESPACE
class QSslConfiguration;
class QHttp2Configuration;
+class QHttp1Configuration;
class QNetworkRequestPrivate;
class Q_NETWORK_EXPORT QNetworkRequest
{
+ Q_GADGET
public:
enum KnownHeaders {
ContentTypeHeader,
@@ -68,8 +35,11 @@ public:
IfModifiedSinceHeader,
ETagHeader,
IfMatchHeader,
- IfNoneMatchHeader
+ IfNoneMatchHeader,
+ NumKnownHeaders
};
+ Q_ENUM(KnownHeaders)
+
enum Attribute {
HttpStatusCodeAttribute,
HttpReasonPhraseAttribute,
@@ -89,23 +59,18 @@ public:
DownloadBufferAttribute, // internal
SynchronousRequestAttribute, // internal
BackgroundRequestAttribute,
-#if QT_DEPRECATED_SINCE(5, 15)
- SpdyAllowedAttribute,
- SpdyWasUsedAttribute,
-#endif // QT_DEPRECATED_SINCE(5, 15)
- EmitAllUploadProgressSignalsAttribute = BackgroundRequestAttribute + 3,
- FollowRedirectsAttribute,
+ EmitAllUploadProgressSignalsAttribute,
Http2AllowedAttribute,
Http2WasUsedAttribute,
-#if QT_DEPRECATED_SINCE(5, 15)
- HTTP2AllowedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2AllowedAttribute") = Http2AllowedAttribute,
- HTTP2WasUsedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2WasUsedAttribute"),
-#endif // QT_DEPRECATED_SINCE(5, 15)
OriginalContentLengthAttribute,
RedirectPolicyAttribute,
Http2DirectAttribute,
ResourceTypeAttribute, // internal
AutoDeleteReplyOnFinishAttribute,
+ ConnectionCacheExpiryTimeoutSecondsAttribute,
+ Http2CleartextAllowedAttribute,
+ UseCredentialsAttribute,
+ FullLocalServerNameAttribute,
User = 1000,
UserMax = 32767
@@ -135,9 +100,12 @@ public:
};
enum TransferTimeoutConstant {
- TransferTimeoutPreset = 30000
+ DefaultTransferTimeoutConstant = 30000
};
+ static constexpr auto DefaultTransferTimeout =
+ std::chrono::milliseconds(DefaultTransferTimeoutConstant);
+
QNetworkRequest();
explicit QNetworkRequest(const QUrl &url);
QNetworkRequest(const QNetworkRequest &other);
@@ -145,7 +113,7 @@ public:
QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; }
QNetworkRequest &operator=(const QNetworkRequest &other);
- void swap(QNetworkRequest &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkRequest &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkRequest &other) const;
inline bool operator!=(const QNetworkRequest &other) const
@@ -154,14 +122,24 @@ public:
QUrl url() const;
void setUrl(const QUrl &url);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+
// "cooked" headers
QVariant header(KnownHeaders header) const;
void setHeader(KnownHeaders header, const QVariant &value);
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
// attributes
@@ -185,13 +163,26 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+ QHttp1Configuration http1Configuration() const;
+ void setHttp1Configuration(const QHttp1Configuration &configuration);
+
QHttp2Configuration http2Configuration() const;
void setHttp2Configuration(const QHttp2Configuration &configuration);
+ qint64 decompressedSafetyCheckThreshold() const;
+ void setDecompressedSafetyCheckThreshold(qint64 threshold);
+#endif // QT_CONFIG(http)
+
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = TransferTimeoutPreset);
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout);
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
private:
QSharedDataPointer<QNetworkRequestPrivate> d;
friend class QNetworkRequestPrivate;
@@ -199,9 +190,24 @@ private:
Q_DECLARE_SHARED(QNetworkRequest)
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+int QNetworkRequest::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkRequest::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkRequest)
-Q_DECLARE_METATYPE(QNetworkRequest::RedirectPolicy)
+QT_DECL_METATYPE_EXTERN(QNetworkRequest, Q_NETWORK_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy,
+ QNetworkRequest__RedirectPolicy, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 5e18da6d55..7268e1a4aa 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREQUEST_P_H
#define QNETWORKREQUEST_P_H
@@ -52,6 +16,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include "qnetworkrequest.h"
#include "QtCore/qbytearray.h"
#include "QtCore/qlist.h"
@@ -62,6 +27,8 @@
QT_BEGIN_NAMESPACE
+class QNetworkCookie;
+
// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
class QNetworkHeadersPrivate
{
@@ -71,27 +38,52 @@ public:
typedef QHash<QNetworkRequest::KnownHeaders, QVariant> CookedHeadersMap;
typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
- RawHeadersList rawHeaders;
+ mutable struct {
+ RawHeadersList headersList;
+ bool isCached = false;
+ } rawHeaderCache;
+
+ QHttpHeaders httpHeaders;
CookedHeadersMap cookedHeaders;
AttributesMap attributes;
QPointer<QObject> originatingObject;
- RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
- RawHeadersList allRawHeaders() const;
+ const RawHeadersList &allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
+ QByteArray rawHeader(QAnyStringView headerName) const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
- void setAllRawHeaders(const RawHeadersList &list);
void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
- static QDateTime fromHttpDate(const QByteArray &value);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+ void setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value);
+
+ void clearHeaders();
+
+ static QDateTime fromHttpDate(QByteArrayView value);
static QByteArray toHttpDate(const QDateTime &dt);
+ static std::optional<qint64> toInt(QByteArrayView value);
+
+ typedef QList<QNetworkCookie> NetworkCookieList;
+ static QByteArray fromCookieList(const NetworkCookieList &cookies);
+ static std::optional<NetworkCookieList> toSetCookieList(const QList<QByteArray> &values);
+ static std::optional<NetworkCookieList> toCookieList(const QList<QByteArray> &values);
+
+ static RawHeadersList fromHttpToRaw(const QHttpHeaders &headers);
+ static QHttpHeaders fromRawToHttp(const RawHeadersList &raw);
+
private:
- void setRawHeaderInternal(const QByteArray &key, const QByteArray &value);
- void parseAndSetHeader(const QByteArray &key, const QByteArray &value);
+ void invalidateHeaderCache();
+
+ void setCookedFromHttp(const QHttpHeaders &newHeaders);
+ void parseAndSetHeader(QByteArrayView key, QByteArrayView value);
+ void parseAndSetHeader(QNetworkRequest::KnownHeaders key, QByteArrayView value);
+
};
-Q_DECLARE_TYPEINFO(QNetworkHeadersPrivate::RawHeaderPair, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QNetworkHeadersPrivate::RawHeaderPair, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkrequestfactory.cpp b/src/network/access/qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..d9c536cef2
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.cpp
@@ -0,0 +1,721 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qnetworkrequestfactory.h"
+#include "qnetworkrequestfactory_p.h"
+
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QNetworkRequestFactoryPrivate)
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrequestfactory, "qt.network.access.request.factory")
+
+/*!
+ \class QNetworkRequestFactory
+ \since 6.7
+ \ingroup shared
+ \inmodule QtNetwork
+
+ \brief Convenience class for grouping remote server endpoints that share
+ common network request properties.
+
+ \preliminary
+
+ REST servers often have endpoints that require the same headers and other data.
+ Grouping such endpoints with a QNetworkRequestFactory makes it more
+ convenient to issue requests to these endpoints; only the typically
+ varying parts such as \e path and \e query parameters are provided
+ when creating a new request.
+
+ Basic usage steps of QNetworkRequestFactory are as follows:
+ \list
+ \li Instantiation
+ \li Setting the data common to all requests
+ \li Issuing requests
+ \endlist
+
+ An example of usage:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 0
+*/
+
+/*!
+ Creates a new QNetworkRequestFactory object.
+ Use setBaseUrl() to set a valid base URL for the requests.
+
+ \sa QNetworkRequestFactory(const QUrl &baseUrl), setBaseUrl()
+*/
+
+QNetworkRequestFactory::QNetworkRequestFactory()
+ : d(new QNetworkRequestFactoryPrivate)
+{
+}
+
+/*!
+ Creates a new QNetworkRequestFactory object, initializing the base URL to
+ \a baseUrl. The base URL is used to populate subsequent network
+ requests.
+
+ If the URL contains a \e path component, it will be extracted and used
+ as a base path in subsequent network requests. This means that any
+ paths provided when requesting individual requests will be appended
+ to this base path, as illustrated below:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 1
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QUrl &baseUrl)
+ : d(new QNetworkRequestFactoryPrivate(baseUrl))
+{
+}
+
+/*!
+ Destroys this QNetworkRequestFactory object.
+ */
+QNetworkRequestFactory::~QNetworkRequestFactory()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ Creates a copy of \a other and returns a reference to this factory.
+ */
+QNetworkRequestFactory &QNetworkRequestFactory::operator=(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ \fn QNetworkRequestFactory::QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept
+
+ Move-constructs the factory from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QNetworkRequestFactory &QNetworkRequestFactory::operator=(QNetworkRequestFactory &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this factory.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+ */
+
+/*!
+ \fn void QNetworkRequestFactory::swap(QNetworkRequestFactory &other)
+
+ Swaps this factory with \a other. This operation is
+ very fast and never fails.
+ */
+
+/*!
+ Returns the base URL used for the individual requests.
+
+ The base URL may contain a path component. This path is used
+ as path "prefix" for the paths that are provided when generating
+ individual requests.
+
+ \sa setBaseUrl()
+ */
+QUrl QNetworkRequestFactory::baseUrl() const
+{
+ return d->baseUrl;
+}
+
+/*!
+ Sets the base URL used in individual requests to \a url.
+
+ \sa baseUrl()
+ */
+void QNetworkRequestFactory::setBaseUrl(const QUrl &url)
+{
+ if (d->baseUrl == url)
+ return;
+
+ d.detach();
+ d->baseUrl = url;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ Returns the SSL configuration set to this factory. The SSL configuration
+ is set to each individual request.
+
+ \sa setSslConfiguration()
+ */
+QSslConfiguration QNetworkRequestFactory::sslConfiguration() const
+{
+ return d->sslConfig;
+}
+
+/*!
+ Sets the SSL configuration to \a configuration.
+
+ \sa sslConfiguration()
+ */
+void QNetworkRequestFactory::setSslConfiguration(const QSslConfiguration &configuration)
+{
+ if (d->sslConfig == configuration)
+ return;
+
+ d.detach();
+ d->sslConfig = configuration;
+}
+#endif
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request is filled with the data that this factory
+ has been configured with.
+
+ \sa createRequest(const QUrlQuery&), createRequest(const QString&, const QUrlQuery&)
+*/
+
+QNetworkRequest QNetworkRequestFactory::createRequest() const
+{
+ return d->newRequest(d->requestUrl());
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a path
+ to the baseUrl (which may itself have a path component).
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path) const
+{
+ return d->newRequest(d->requestUrl(&path));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a query
+ to the baseUrl.
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(nullptr, &query));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned requests URL is formed by appending the provided \a path
+ and \a query to the baseUrl (which may have a path component).
+
+ If the provided \a path contains query items, they will be combined
+ with the items in \a query.
+
+ \sa createRequest(const QUrlQuery&), createRequest(), baseUrl()
+ */
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path, const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(&path, &query));
+}
+
+/*!
+ Sets \a headers that are common to all requests.
+
+ These headers are added to individual requests' headers.
+ This is a convenience mechanism for setting headers that
+ repeat across requests.
+
+ \sa commonHeaders(), clearCommonHeaders(), createRequest()
+ */
+void QNetworkRequestFactory::setCommonHeaders(const QHttpHeaders &headers)
+{
+ d.detach();
+ d->headers = headers;
+}
+
+/*!
+ Returns the currently set headers.
+
+ \sa setCommonHeaders(), clearCommonHeaders()
+ */
+QHttpHeaders QNetworkRequestFactory::commonHeaders() const
+{
+ return d->headers;
+}
+
+/*!
+ Clears current headers.
+
+ \sa commonHeaders(), setCommonHeaders()
+*/
+void QNetworkRequestFactory::clearCommonHeaders()
+{
+ if (d->headers.isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+/*!
+ Returns the bearer token that has been set.
+
+ The bearer token, if present, is used to set the
+ \c {Authorization: Bearer my_token} header for requests. This is a common
+ authorization convention and is provided as an additional convenience.
+
+ The means to acquire the bearer token vary. Standard methods include \c OAuth2
+ and the service provider's website/dashboard. It is expected that the bearer
+ token changes over time. For example, when updated with a refresh token,
+ always setting the new token again ensures that subsequent requests have
+ the latest, valid token.
+
+ The presence of the bearer token does not impact the \l commonHeaders()
+ listing. If the \l commonHeaders() also lists \c Authorization header, it
+ will be overwritten.
+
+ \sa setBearerToken(), commonHeaders()
+ */
+QByteArray QNetworkRequestFactory::bearerToken() const
+{
+ return d->bearerToken;
+}
+
+/*!
+ Sets the bearer token to \a token.
+
+ \sa bearerToken(), clearBearerToken()
+*/
+void QNetworkRequestFactory::setBearerToken(const QByteArray &token)
+{
+ if (d->bearerToken == token)
+ return;
+
+ d.detach();
+ d->bearerToken = token;
+}
+
+/*!
+ Clears the bearer token.
+
+ \sa bearerToken()
+*/
+void QNetworkRequestFactory::clearBearerToken()
+{
+ if (d->bearerToken.isEmpty())
+ return;
+
+ d.detach();
+ d->bearerToken.clear();
+}
+
+/*!
+ Returns the username set to this factory.
+
+ \sa setUserName(), clearUserName(), password()
+*/
+QString QNetworkRequestFactory::userName() const
+{
+ return d->userName;
+}
+
+/*!
+ Sets the username of this factory to \a userName.
+
+ The username is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa userName(), clearUserName(), password()
+*/
+void QNetworkRequestFactory::setUserName(const QString &userName)
+{
+ if (d->userName == userName)
+ return;
+ d.detach();
+ d->userName = userName;
+}
+
+/*!
+ Clears the username set to this factory.
+*/
+void QNetworkRequestFactory::clearUserName()
+{
+ if (d->userName.isEmpty())
+ return;
+ d.detach();
+ d->userName.clear();
+}
+
+/*!
+ Returns the password set to this factory.
+
+ \sa password(), clearPassword(), userName()
+*/
+QString QNetworkRequestFactory::password() const
+{
+ return d->password;
+}
+
+/*!
+ Sets the password of this factory to \a password.
+
+ The password is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa password(), clearPassword(), userName()
+*/
+void QNetworkRequestFactory::setPassword(const QString &password)
+{
+ if (d->password == password)
+ return;
+ d.detach();
+ d->password = password;
+}
+
+/*!
+ Clears the password set to this factory.
+
+ \sa password(), setPassword(), userName()
+*/
+void QNetworkRequestFactory::clearPassword()
+{
+ if (d->password.isEmpty())
+ return;
+ d.detach();
+ d->password.clear();
+}
+
+/*!
+ Sets \a timeout used for transfers.
+
+ \sa transferTimeout(), QNetworkRequest::setTransferTimeout(),
+ QNetworkAccessManager::setTransferTimeout()
+*/
+void QNetworkRequestFactory::setTransferTimeout(std::chrono::milliseconds timeout)
+{
+ if (d->transferTimeout == timeout)
+ return;
+
+ d.detach();
+ d->transferTimeout = timeout;
+}
+
+/*!
+ Returns the timeout used for transfers.
+
+ \sa setTransferTimeout(), QNetworkRequest::transferTimeout(),
+ QNetworkAccessManager::transferTimeout()
+*/
+std::chrono::milliseconds QNetworkRequestFactory::transferTimeout() const
+{
+ return d->transferTimeout;
+}
+
+/*!
+ Returns query parameters that are added to individual requests' query
+ parameters. The query parameters are added to any potential query
+ parameters provided with the individual \l createRequest() calls.
+
+ Use cases for using repeating query parameters are server dependent,
+ but typical examples include language setting \c {?lang=en}, format
+ specification \c {?format=json}, API version specification
+ \c {?version=1.0} and API key authentication.
+
+ \sa setQueryParameters(), clearQueryParameters(), createRequest()
+*/
+QUrlQuery QNetworkRequestFactory::queryParameters() const
+{
+ return d->queryParameters;
+}
+
+/*!
+ Sets \a query parameters that are added to individual requests' query
+ parameters.
+
+ \sa queryParameters(), clearQueryParameters()
+ */
+void QNetworkRequestFactory::setQueryParameters(const QUrlQuery &query)
+{
+ if (d->queryParameters == query)
+ return;
+
+ d.detach();
+ d->queryParameters = query;
+}
+
+/*!
+ Clears the query parameters.
+
+ \sa queryParameters()
+*/
+void QNetworkRequestFactory::clearQueryParameters()
+{
+ if (d->queryParameters.isEmpty())
+ return;
+
+ d.detach();
+ d->queryParameters.clear();
+}
+
+/*!
+ \since 6.8
+
+ Sets the priority for any future requests created by this factory to
+ \a priority.
+
+ The default priority is \l QNetworkRequest::NormalPriority.
+
+ \sa priority(), QNetworkRequest::setPriority()
+*/
+void QNetworkRequestFactory::setPriority(QNetworkRequest::Priority priority)
+{
+ if (d->priority == priority)
+ return;
+ d.detach();
+ d->priority = priority;
+}
+
+/*!
+ \since 6.8
+
+ Returns the priority assigned to any future requests created by this
+ factory.
+
+ \sa setPriority(), QNetworkRequest::priority()
+*/
+QNetworkRequest::Priority QNetworkRequestFactory::priority() const
+{
+ return d->priority;
+}
+
+/*!
+ \since 6.8
+
+ Sets the value associated with \a attribute to \a value.
+ If the attribute is already set, the previous value is
+ replaced. The attributes are set to any future requests
+ created by this factory.
+
+ \sa attribute(), clearAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+void QNetworkRequestFactory::setAttribute(QNetworkRequest::Attribute attribute,
+ const QVariant &value)
+{
+ if (attribute == QNetworkRequest::HttpStatusCodeAttribute
+ || attribute == QNetworkRequest::HttpReasonPhraseAttribute
+ || attribute == QNetworkRequest::RedirectionTargetAttribute
+ || attribute == QNetworkRequest::ConnectionEncryptedAttribute
+ || attribute == QNetworkRequest::SourceIsFromCacheAttribute
+ || attribute == QNetworkRequest::HttpPipeliningWasUsedAttribute
+ || attribute == QNetworkRequest::Http2WasUsedAttribute
+ || attribute == QNetworkRequest::OriginalContentLengthAttribute)
+ {
+ qCWarning(lcQrequestfactory, "%i is a reply-only attribute, ignoring.", attribute);
+ return;
+ }
+ d.detach();
+ d->attributes.insert(attribute, value);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns a default-constructed \l QVariant.
+
+ \sa attribute(QNetworkRequest::Attribute, const QVariant &),
+ setAttribute(), clearAttributes(), QNetworkRequest::Attribute
+
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute) const
+{
+ return d->attributes.value(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns \a defaultValue.
+
+ \sa attribute(), setAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const
+{
+ return d->attributes.value(attribute, defaultValue);
+}
+
+/*!
+ \since 6.8
+
+ Clears \a attribute set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttribute(QNetworkRequest::Attribute attribute)
+{
+ if (!d->attributes.contains(attribute))
+ return;
+ d.detach();
+ d->attributes.remove(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Clears any attributes set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttributes()
+{
+ if (d->attributes.isEmpty())
+ return;
+ d.detach();
+ d->attributes.clear();
+}
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate(const QUrl &baseUrl)
+ : baseUrl(baseUrl)
+{
+}
+
+QNetworkRequestFactoryPrivate::~QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequest QNetworkRequestFactoryPrivate::newRequest(const QUrl &url) const
+{
+ QNetworkRequest request;
+ request.setUrl(url);
+#if QT_CONFIG(ssl)
+ if (!sslConfig.isNull())
+ request.setSslConfiguration(sslConfig);
+#endif
+ auto h = headers;
+ constexpr char Bearer[] = "Bearer ";
+ if (!bearerToken.isEmpty())
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::Authorization, Bearer + bearerToken);
+ request.setHeaders(std::move(h));
+
+ request.setTransferTimeout(transferTimeout);
+ request.setPriority(priority);
+
+ for (const auto &[attribute, value] : attributes.asKeyValueRange())
+ request.setAttribute(attribute, value);
+
+ return request;
+}
+
+QUrl QNetworkRequestFactoryPrivate::requestUrl(const QString *path,
+ const QUrlQuery *query) const
+{
+ const QUrl providedPath = path ? QUrl(*path) : QUrl{};
+ const QUrlQuery providedQuery = query ? *query : QUrlQuery();
+
+ if (!providedPath.scheme().isEmpty() || !providedPath.host().isEmpty()) {
+ qCWarning(lcQrequestfactory, "The provided path %ls may only contain path and query item "
+ "components, and other parts will be ignored. Set the baseUrl instead",
+ qUtf16Printable(providedPath.toDisplayString()));
+ }
+
+ QUrl resultUrl = baseUrl;
+ QUrlQuery resultQuery(providedQuery);
+ QString basePath = baseUrl.path();
+
+ resultUrl.setUserName(userName);
+ resultUrl.setPassword(password);
+
+ // Separate the path and query parameters components on the application-provided path
+ const QString requestPath{providedPath.path()};
+ const QUrlQuery pathQueryItems{providedPath};
+
+ if (!pathQueryItems.isEmpty()) {
+ // Add any query items provided as part of the path
+ const auto items = pathQueryItems.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &[key, value]: items)
+ resultQuery.addQueryItem(key, value);
+ }
+
+ if (!queryParameters.isEmpty()) {
+ // Add any query items set to this factory
+ const QList<std::pair<QString,QString>> items =
+ queryParameters.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &item: items)
+ resultQuery.addQueryItem(item.first, item.second);
+ }
+
+ if (!resultQuery.isEmpty())
+ resultUrl.setQuery(resultQuery);
+
+ if (requestPath.isEmpty())
+ return resultUrl;
+
+ // Ensure that the "base path" (the path that may be present
+ // in the baseUrl), and the request path are joined with one '/'
+ // If both have it, remove one, if neither has it, add one
+ if (basePath.endsWith(u'/') && requestPath.startsWith(u'/'))
+ basePath.chop(1);
+ else if (!requestPath.startsWith(u'/') && !basePath.endsWith(u'/'))
+ basePath.append(u'/');
+
+ resultUrl.setPath(basePath.append(requestPath));
+ return resultUrl;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QNetworkRequestFactory::operator<<(QDebug debug,
+ const QNetworkRequestFactory &factory)
+
+ Writes \a factory into \a debug stream.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QNetworkRequestFactory &factory)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QNetworkRequestFactory(baseUrl = " << factory.baseUrl()
+ << ", headers = " << factory.commonHeaders()
+ << ", queryParameters = " << factory.queryParameters().queryItems()
+ << ", bearerToken = " << (factory.bearerToken().isEmpty() ? "(empty)" : "(is set)")
+ << ", transferTimeout = " << factory.transferTimeout()
+ << ", userName = " << (factory.userName().isEmpty() ? "(empty)" : "(is set)")
+ << ", password = " << (factory.password().isEmpty() ? "(empty)" : "(is set)")
+#if QT_CONFIG(ssl)
+ << ", SSL configuration"
+ << (factory.sslConfiguration().isNull() ? " is not set (default)" : " is set")
+#else
+ << ", no SSL support"
+#endif
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkrequestfactory.h b/src/network/access/qnetworkrequestfactory.h
new file mode 100644
index 0000000000..9d955a51e7
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_H
+#define QNETWORKREQUESTFACTORY_H
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+#if QT_CONFIG(ssl)
+class QSslConfiguration;
+#endif
+
+class QNetworkRequestFactoryPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QNetworkRequestFactoryPrivate, Q_NETWORK_EXPORT)
+
+class QT_TECH_PREVIEW_API QNetworkRequestFactory
+{
+public:
+ Q_NETWORK_EXPORT QNetworkRequestFactory();
+ Q_NETWORK_EXPORT explicit QNetworkRequestFactory(const QUrl &baseUrl);
+ Q_NETWORK_EXPORT ~QNetworkRequestFactory();
+
+ Q_NETWORK_EXPORT QNetworkRequestFactory(const QNetworkRequestFactory &other);
+ QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept = default;
+ Q_NETWORK_EXPORT QNetworkRequestFactory &operator=(const QNetworkRequestFactory &other);
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QNetworkRequestFactory)
+ void swap(QNetworkRequestFactory &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT QUrl baseUrl() const;
+ Q_NETWORK_EXPORT void setBaseUrl(const QUrl &url);
+
+#if QT_CONFIG(ssl)
+ Q_NETWORK_EXPORT QSslConfiguration sslConfiguration() const;
+ Q_NETWORK_EXPORT void setSslConfiguration(const QSslConfiguration &configuration);
+#endif
+
+ Q_NETWORK_EXPORT QNetworkRequest createRequest() const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QUrlQuery &query) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path, const QUrlQuery &query) const;
+
+ Q_NETWORK_EXPORT void setCommonHeaders(const QHttpHeaders &headers);
+ Q_NETWORK_EXPORT QHttpHeaders commonHeaders() const;
+ Q_NETWORK_EXPORT void clearCommonHeaders();
+
+ Q_NETWORK_EXPORT QByteArray bearerToken() const;
+ Q_NETWORK_EXPORT void setBearerToken(const QByteArray &token);
+ Q_NETWORK_EXPORT void clearBearerToken();
+
+ Q_NETWORK_EXPORT QString userName() const;
+ Q_NETWORK_EXPORT void setUserName(const QString &userName);
+ Q_NETWORK_EXPORT void clearUserName();
+
+ Q_NETWORK_EXPORT QString password() const;
+ Q_NETWORK_EXPORT void setPassword(const QString &password);
+ Q_NETWORK_EXPORT void clearPassword();
+
+ Q_NETWORK_EXPORT void setTransferTimeout(std::chrono::milliseconds timeout);
+ Q_NETWORK_EXPORT std::chrono::milliseconds transferTimeout() const;
+
+ Q_NETWORK_EXPORT QUrlQuery queryParameters() const;
+ Q_NETWORK_EXPORT void setQueryParameters(const QUrlQuery &query);
+ Q_NETWORK_EXPORT void clearQueryParameters();
+
+ Q_NETWORK_EXPORT void setPriority(QNetworkRequest::Priority priority);
+ Q_NETWORK_EXPORT QNetworkRequest::Priority priority() const;
+
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute) const;
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const;
+ Q_NETWORK_EXPORT void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
+ Q_NETWORK_EXPORT void clearAttribute(QNetworkRequest::Attribute attribute);
+ Q_NETWORK_EXPORT void clearAttributes();
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkRequestFactory &reply);
+#endif
+
+ QExplicitlySharedDataPointer<QNetworkRequestFactoryPrivate> d;
+};
+
+Q_DECLARE_SHARED(QNetworkRequestFactory)
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_H
diff --git a/src/network/access/qnetworkrequestfactory_p.h b/src/network/access/qnetworkrequestfactory_p.h
new file mode 100644
index 0000000000..4116669f21
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_P_H
+#define QNETWORKREQUESTFACTORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access framework. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/qhttpheaders.h>
+#include <QtNetwork/qnetworkrequest.h>
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+#include <QtCore/qhash.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkRequestFactoryPrivate : public QSharedData
+{
+public:
+ QNetworkRequestFactoryPrivate();
+ explicit QNetworkRequestFactoryPrivate(const QUrl &baseUrl);
+ ~QNetworkRequestFactoryPrivate();
+ QNetworkRequest newRequest(const QUrl &url) const;
+ QUrl requestUrl(const QString *path = nullptr, const QUrlQuery *query = nullptr) const;
+
+#if QT_CONFIG(ssl)
+ QSslConfiguration sslConfig;
+#endif
+ QUrl baseUrl;
+ QHttpHeaders headers;
+ QByteArray bearerToken;
+ QString userName;
+ QString password;
+ QUrlQuery queryParameters;
+ QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority;
+ std::chrono::milliseconds transferTimeout{0};
+ QHash<QNetworkRequest::Attribute, QVariant> attributes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_P_H
diff --git a/src/network/access/qrestaccessmanager.cpp b/src/network/access/qrestaccessmanager.cpp
new file mode 100644
index 0000000000..7ef682e955
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.cpp
@@ -0,0 +1,828 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestaccessmanager.h"
+#include "qrestaccessmanager_p.h"
+#include "qrestreply.h"
+
+#include <QtNetwork/qhttpmultipart.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
+
+/*!
+
+ \class QRestAccessManager
+ \brief The QRestAccessManager is a convenience wrapper for
+ QNetworkAccessManager.
+ \since 6.7
+
+ \ingroup network
+ \inmodule QtNetwork
+ \reentrant
+
+ \preliminary
+
+ QRestAccessManager is a convenience wrapper on top of
+ QNetworkAccessManager. It amends datatypes and HTTP methods
+ that are useful for typical RESTful client applications.
+
+ The usual Qt networking features are accessible by configuring the
+ wrapped QNetworkAccessManager directly. QRestAccessManager does not
+ take ownership of the wrapped QNetworkAccessManager.
+
+ QRestAccessManager and related QRestReply classes can only be used in the
+ thread they live in. See \l {QObject#Thread Affinity}{QObject thread affinity}
+ for more information.
+
+ \section1 Issuing Network Requests and Handling Replies
+
+ Network requests are initiated with a function call corresponding to
+ the desired HTTP method, such as \c get() and \c post().
+
+ \section2 Using Signals and Slots
+
+ The function returns a QNetworkReply* object, whose signals can be used
+ to follow up on the completion of the request in a traditional
+ Qt-signals-and-slots way.
+
+ Here's an example of how you could send a GET request and handle the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 0
+
+ \section2 Using Callbacks and Context Objects
+
+ The functions also take a context object of QObject (subclass) type
+ and a callback function as parameters. The callback takes one QRestReply&
+ as a parameter. The callback can be any callable, including a
+ pointer-to-member-function.
+
+ These callbacks are invoked when the reply has finished processing
+ (also in the case the processing finished due to an error).
+
+ The context object can be \c nullptr, although, generally speaking,
+ this is discouraged. Using a valid context object ensures that if the
+ context object is destroyed during request processing, the callback will
+ not be called. Stray callbacks which access a destroyed context is a source
+ of application misbehavior.
+
+ Here's an example of how you could send a GET request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 1
+
+ Many of the functions take in data for sending to a server. The data is
+ supplied as the second parameter after the request.
+
+ Here's an example of how you could send a POST request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 2
+
+ The provided QRestReply& is valid only while the callback
+ executes. If you need it for longer, you can either move it
+ to another QRestReply, or construct a new one and initialize
+ it with the QNetworkReply (see QRestReply::networkReply()).
+
+ \section2 Supported data types
+
+ The following table summarizes the methods and the supported data types.
+ \c X means support.
+
+ \table
+ \header
+ \li Data type
+ \li \c get()
+ \li \c post()
+ \li \c put()
+ \li \c head()
+ \li \c patch()
+ \li \c deleteResource()
+ \li \c sendCustomRequest()
+ \row
+ \li No data
+ \li X
+ \li -
+ \li -
+ \li X
+ \li -
+ \li X
+ \li -
+ \row
+ \li QByteArray
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \row
+ \li QJsonDocument *)
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QVariantMap **)
+ \li -
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QHttpMultiPart
+ \li -
+ \li X
+ \li X
+ \li -
+ \li -
+ \li -
+ \li X
+ \row
+ \li QIODevice
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \endtable
+
+ *) QJsonDocument is sent in \l QJsonDocument::Compact format,
+ and the \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply, QNetworkRequestFactory, QNetworkAccessManager
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 3
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request and provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 4
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP POST} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 5
+
+ Alternatively, the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c post() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PUT} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 6
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c put() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PATCH} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 10
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c patch() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::head(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP HEAD} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 7
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c head() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::deleteResource(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP DELETE} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 8
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c deleteResource() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues \a request based HTTP request with custom \a method and the
+ provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 9
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ Constructs a QRestAccessManager object and sets \a parent as the parent
+ object, and \a manager as the underlying QNetworkAccessManager which
+ is used for communication.
+
+ \sa networkAccessManager()
+*/
+
+QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
+ : QObject(*new QRestAccessManagerPrivate, parent)
+{
+ Q_D(QRestAccessManager);
+ d->qnam = manager;
+ if (!d->qnam)
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
+}
+
+/*!
+ Destroys the QRestAccessManager object.
+*/
+QRestAccessManager::~QRestAccessManager()
+ = default;
+
+/*!
+ Returns the underlying QNetworkAccessManager instance.
+
+ \sa QNetworkAccessManager
+*/
+QNetworkAccessManager *QRestAccessManager::networkAccessManager() const
+{
+ Q_D(const QRestAccessManager);
+ return d->qnam;
+}
+
+QRestAccessManagerPrivate::QRestAccessManagerPrivate()
+ = default;
+
+QRestAccessManagerPrivate::~QRestAccessManagerPrivate()
+{
+ if (!activeRequests.isEmpty()) {
+ qCWarning(lcQrest, "Access manager destroyed while %lld requests were still in progress",
+ qlonglong(activeRequests.size()));
+ }
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->post(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return postWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->get(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->deleteResource(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->head(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->put(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return putWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+static const auto PATCH = "PATCH"_ba;
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest(
+ [](auto qnam, auto req, auto data) { return qnam->sendCustomRequest(req, PATCH, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return patchWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, const QByteArray &data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QIODevice *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QHttpMultiPart *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *reply,
+ const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_Q(QRestAccessManager);
+ Q_ASSERT(reply);
+ QtPrivate::SlotObjSharedPtr slotPtr(QtPrivate::SlotObjUniquePtr{slot}); // adopts
+ activeRequests.insert(reply, CallerInfo{contextObject, slotPtr});
+ // The signal connections below are made to 'q' to avoid stray signal
+ // handling upon its destruction while requests were still in progress
+
+ QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
+ handleReplyFinished(reply);
+ });
+ // Safe guard in case reply is destroyed before it's finished
+ QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ // If context object is destroyed, clean up any possible replies it had associated with it
+ if (contextObject) {
+ QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ }
+ return reply;
+}
+
+void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
+{
+ Q_Q(QRestAccessManager);
+ if (QThread::currentThread() != q->thread()) {
+ qCWarning(lcQrest, "QRestAccessManager can only be called in the thread it belongs to");
+ Q_ASSERT(false);
+ }
+ if (contextObject && (contextObject->thread() != q->thread())) {
+ qCWarning(lcQrest, "QRestAccessManager: the context object must reside in the same thread");
+ Q_ASSERT(false);
+ }
+}
+
+QNetworkReply* QRestAccessManagerPrivate::warnNoAccessManager()
+{
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
+ return nullptr;
+}
+
+void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
+{
+ auto request = activeRequests.find(reply);
+ if (request == activeRequests.end()) {
+ qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
+ return;
+ }
+
+ CallerInfo caller = request.value();
+ activeRequests.erase(request);
+
+ if (caller.slot) {
+ // Callback was provided
+ QRestReply restReply(reply);
+ void *argv[] = { nullptr, &restReply };
+ // If we have context object, use it
+ QObject *context = caller.contextObject
+ ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
+ caller.slot->call(context, argv);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qrestaccessmanager.cpp"
diff --git a/src/network/access/qrestaccessmanager.h b/src/network/access/qrestaccessmanager.h
new file mode 100644
index 0000000000..3245b41785
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_H
+#define QRESTACCESSMANAGER_H
+
+#if 0
+#pragma qt_class(QRestAccessManager)
+#endif
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+class QRestReply;
+
+#define QREST_METHOD_WITH_DATA(METHOD, DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##WithDataImpl(request, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data) \
+{ \
+ return METHOD##WithDataImpl(request, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_NO_DATA(METHOD) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##NoDataImpl(request, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request) \
+{ \
+ return METHOD##NoDataImpl(request, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##NoDataImpl(const QNetworkRequest &request, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return customWithDataImpl(request, method, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \
+{ \
+ return customWithDataImpl(request, method, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \
+ DATA data, const QObject* context, \
+ QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+class QRestAccessManagerPrivate;
+class QT_TECH_PREVIEW_API Q_NETWORK_EXPORT QRestAccessManager : public QObject
+{
+ Q_OBJECT
+ using CallbackPrototype = void(*)(QRestReply&);
+ template <typename Functor>
+ using ContextTypeForFunctor = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
+ template <typename Functor>
+ using if_compatible_callback = std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<CallbackPrototype, Functor>::value, bool>;
+public:
+ explicit QRestAccessManager(QNetworkAccessManager *manager, QObject *parent = nullptr);
+ ~QRestAccessManager() override;
+
+ QNetworkAccessManager *networkAccessManager() const;
+
+ QREST_METHOD_NO_DATA(deleteResource)
+ QREST_METHOD_NO_DATA(head)
+ QREST_METHOD_NO_DATA(get)
+ QREST_METHOD_WITH_DATA(get, const QByteArray &)
+ QREST_METHOD_WITH_DATA(get, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(get, QIODevice *)
+ QREST_METHOD_WITH_DATA(post, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(post, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(post, const QByteArray &)
+ QREST_METHOD_WITH_DATA(post, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(post, QIODevice *)
+ QREST_METHOD_WITH_DATA(put, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(put, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(put, const QByteArray &)
+ QREST_METHOD_WITH_DATA(put, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(put, QIODevice *)
+ QREST_METHOD_WITH_DATA(patch, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(patch, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(patch, const QByteArray &)
+ QREST_METHOD_WITH_DATA(patch, QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &)
+ QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)
+
+private:
+ Q_DECLARE_PRIVATE(QRestAccessManager)
+ Q_DISABLE_COPY(QRestAccessManager)
+};
+
+#undef QREST_METHOD_NO_DATA
+#undef QREST_METHOD_WITH_DATA
+#undef QREST_METHOD_CUSTOM_WITH_DATA
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestaccessmanager_p.h b/src/network/access/qrestaccessmanager_p.h
new file mode 100644
index 0000000000..2e6c1afb90
--- /dev/null
+++ b/src/network/access/qrestaccessmanager_p.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_P_H
+#define QRESTACCESSMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrestaccessmanager.h"
+#include "private/qobject_p.h"
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRestReply;
+class QRestAccessManagerPrivate : public QObjectPrivate
+{
+public:
+ QRestAccessManagerPrivate();
+ ~QRestAccessManagerPrivate() override;
+
+ QNetworkReply* createActiveRequest(QNetworkReply *reply, const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot);
+ void handleReplyFinished(QNetworkReply *reply);
+
+ using ReqOpRef = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*) const>;
+ QNetworkReply *executeRequest(ReqOpRef requestOperation,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkReply *reply = requestOperation(qnam);
+ return createActiveRequest(reply, context, slot);
+ }
+
+ using ReqOpRefJson = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*,
+ const QNetworkRequest &,
+ const QByteArray &) const>;
+ QNetworkReply *executeRequest(ReqOpRefJson requestOperation, const QJsonDocument &jsonDoc,
+ const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkRequest req(request);
+ auto h = req.headers();
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
+ h.append(QHttpHeaders::WellKnownHeader::ContentType,
+ QLatin1StringView{"application/json"});
+ }
+ req.setHeaders(std::move(h));
+ QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
+ return createActiveRequest(reply, context, slot);
+ }
+
+ void verifyThreadAffinity(const QObject *contextObject);
+ Q_DECL_COLD_FUNCTION
+ QNetworkReply* warnNoAccessManager();
+
+ struct CallerInfo {
+ QPointer<const QObject> contextObject = nullptr;
+ QtPrivate::SlotObjSharedPtr slot;
+ };
+ QHash<QNetworkReply*, CallerInfo> activeRequests;
+
+ QNetworkAccessManager *qnam = nullptr;
+ bool deletesRepliesOnFinished = true;
+ Q_DECLARE_PUBLIC(QRestAccessManager)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestreply.cpp b/src/network/access/qrestreply.cpp
new file mode 100644
index 0000000000..2d8d101084
--- /dev/null
+++ b/src/network/access/qrestreply.cpp
@@ -0,0 +1,608 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestreply.h"
+#include "qrestreply_p.h"
+
+#include <QtNetwork/private/qnetworkreply_p.h>
+
+#include <QtCore/qbytearrayview.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qlatin1stringmatcher.h>
+#include <QtCore/qlatin1stringview.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstringconverter.h>
+
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+Q_DECLARE_LOGGING_CATEGORY(lcQrest)
+
+/*!
+ \class QRestReply
+ \since 6.7
+ \brief QRestReply is a convenience wrapper for QNetworkReply.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ \preliminary
+
+ QRestReply wraps a QNetworkReply and provides convenience methods for data
+ and status handling. The methods provide convenience for typical RESTful
+ client applications.
+
+ QRestReply doesn't take ownership of the wrapped QNetworkReply, and the
+ lifetime and ownership of the reply is as defined by QNetworkAccessManager
+ documentation.
+
+ QRestReply object is not copyable, but is movable.
+
+ \sa QRestAccessManager, QNetworkReply, QNetworkAccessManager,
+ QNetworkAccessManager::setAutoDeleteReplies()
+*/
+
+/*!
+ Creates a QRestReply and initializes the wrapped QNetworkReply to \a reply.
+*/
+QRestReply::QRestReply(QNetworkReply *reply)
+ : wrapped(reply)
+{
+ if (!wrapped)
+ qCWarning(lcQrest, "QRestReply: QNetworkReply is nullptr");
+}
+
+/*!
+ Destroys this QRestReply object.
+*/
+QRestReply::~QRestReply()
+{
+ delete d;
+}
+
+/*!
+ \fn QRestReply::QRestReply(QRestReply &&other) noexcept
+
+ Move-constructs the reply from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QRestReply &QRestReply::operator=(QRestReply &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this reply.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Returns a pointer to the underlying QNetworkReply wrapped by this object.
+*/
+QNetworkReply *QRestReply::networkReply() const
+{
+ return wrapped;
+}
+
+/*!
+ Returns the received data as a QJsonDocument.
+
+ The returned value is wrapped in \c std::optional. If the conversion
+ from the received data fails (empty data or JSON parsing error),
+ \c std::nullopt is returned, and \a error is filled with details.
+
+ Calling this function consumes the received data, and any further calls
+ to get response data will return empty.
+
+ This function returns \c {std::nullopt} and will not consume
+ any data if the reply is not finished. If \a error is passed, it will be
+ set to QJsonParseError::NoError to distinguish this case from an actual
+ error.
+
+ \sa readBody(), readText()
+*/
+std::optional<QJsonDocument> QRestReply::readJson(QJsonParseError *error)
+{
+ if (!wrapped) {
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+
+ if (!wrapped->isFinished()) {
+ qCWarning(lcQrest, "readJson() called on an unfinished reply, ignoring");
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+ QJsonParseError parseError;
+ const QByteArray data = wrapped->readAll();
+ const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
+ if (error)
+ *error = parseError;
+ if (parseError.error)
+ return std::nullopt;
+ return doc;
+}
+
+/*!
+ Returns the received data as a QByteArray.
+
+ Calling this function consumes the data received so far, and any further
+ calls to get response data will return empty until further data has been
+ received.
+
+ \sa readJson(), readText(), QNetworkReply::bytesAvailable(),
+ QNetworkReply::readyRead()
+*/
+QByteArray QRestReply::readBody()
+{
+ return wrapped ? wrapped->readAll() : QByteArray{};
+}
+
+/*!
+ Returns the received data as a QString.
+
+ The received data is decoded into a QString (UTF-16). If available, the decoding
+ uses the \e Content-Type header's \e charset parameter to determine the
+ source encoding. If the encoding information is not available or not supported
+ by \l QStringConverter, UTF-8 is used by default.
+
+ Calling this function consumes the data received so far. Returns
+ a default constructed value if no new data is available, or if the
+ decoding is not supported by \l QStringConverter, or if the decoding
+ has errors (for example invalid characters).
+
+ \sa readJson(), readBody(), QNetworkReply::readyRead()
+*/
+QString QRestReply::readText()
+{
+ QString result;
+ if (!wrapped)
+ return result;
+
+ QByteArray data = wrapped->readAll();
+ if (data.isEmpty())
+ return result;
+
+ // Text decoding needs to persist decoding state across calls to this function,
+ // so allocate decoder if not yet allocated.
+ if (!d)
+ d = new QRestReplyPrivate;
+
+ if (!d->decoder) {
+ const QByteArray charset = QRestReplyPrivate::contentCharset(wrapped);
+ d->decoder.emplace(charset.constData());
+ if (!d->decoder->isValid()) { // the decoder may not support the mimetype's charset
+ qCWarning(lcQrest, "readText(): Charset \"%s\" is not supported", charset.constData());
+ return result;
+ }
+ }
+ // Check if the decoder already had an error, or has errors after decoding current data chunk
+ if (d->decoder->hasError() || (result = (*d->decoder)(data), d->decoder->hasError())) {
+ qCWarning(lcQrest, "readText(): Decoding error occurred");
+ return {};
+ }
+ return result;
+}
+
+/*!
+ Returns the HTTP status received in the server response.
+ The value is \e 0 if not available (the status line has not been received,
+ yet).
+
+ \note The HTTP status is reported as indicated by the received HTTP
+ response. An error() may occur after receiving the status, for instance
+ due to network disconnection while receiving a long response.
+ These potential subsequent errors are not represented by the reported
+ HTTP status.
+
+ \sa isSuccess(), hasError(), error()
+*/
+int QRestReply::httpStatus() const
+{
+ return wrapped ? wrapped->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : 0;
+}
+
+/*!
+ \fn bool QRestReply::isSuccess() const
+
+ Returns whether the HTTP status is between 200..299 and no
+ further errors have occurred while receiving the response (for example,
+ abrupt disconnection while receiving the body data). This function
+ is a convenient way to check whether the response is considered successful.
+
+ \sa httpStatus(), hasError(), error()
+*/
+
+/*!
+ Returns whether the HTTP status is between 200..299.
+
+ \sa isSuccess(), httpStatus(), hasError(), error()
+*/
+bool QRestReply::isHttpStatusSuccess() const
+{
+ const int status = httpStatus();
+ return status >= 200 && status < 300;
+}
+
+/*!
+ Returns whether an error has occurred. This includes errors such as
+ network and protocol errors, but excludes cases where the server
+ successfully responded with an HTTP error status (for example
+ \c {500 Internal Server Error}). Use \l httpStatus() or
+ \l isHttpStatusSuccess() to get the HTTP status information.
+
+ \sa httpStatus(), isSuccess(), error(), errorString()
+*/
+bool QRestReply::hasError() const
+{
+ if (!wrapped)
+ return false;
+
+ const int status = httpStatus();
+ if (status > 0) {
+ // The HTTP status is set upon receiving the response headers, but the
+ // connection might still fail later while receiving the body data.
+ return wrapped->error() == QNetworkReply::RemoteHostClosedError;
+ }
+ return wrapped->error() != QNetworkReply::NoError;
+}
+
+/*!
+ Returns the last error, if any. The errors include
+ errors such as network and protocol errors, but exclude
+ cases when the server successfully responded with an HTTP status.
+
+ \sa httpStatus(), isSuccess(), hasError(), errorString()
+*/
+QNetworkReply::NetworkError QRestReply::error() const
+{
+ if (!hasError())
+ return QNetworkReply::NetworkError::NoError;
+ return wrapped->error();
+}
+
+/*!
+ Returns a human-readable description of the last network error.
+
+ \sa httpStatus(), isSuccess(), hasError(), error()
+*/
+QString QRestReply::errorString() const
+{
+ if (hasError())
+ return wrapped->errorString();
+ return {};
+}
+
+QRestReplyPrivate::QRestReplyPrivate()
+ = default;
+
+QRestReplyPrivate::~QRestReplyPrivate()
+ = default;
+
+#ifndef QT_NO_DEBUG_STREAM
+static QLatin1StringView operationName(QNetworkAccessManager::Operation operation)
+{
+ switch (operation) {
+ case QNetworkAccessManager::Operation::GetOperation:
+ return "GET"_L1;
+ case QNetworkAccessManager::Operation::HeadOperation:
+ return "HEAD"_L1;
+ case QNetworkAccessManager::Operation::PostOperation:
+ return "POST"_L1;
+ case QNetworkAccessManager::Operation::PutOperation:
+ return "PUT"_L1;
+ case QNetworkAccessManager::Operation::DeleteOperation:
+ return "DELETE"_L1;
+ case QNetworkAccessManager::Operation::CustomOperation:
+ return "CUSTOM"_L1;
+ case QNetworkAccessManager::Operation::UnknownOperation:
+ return "UNKNOWN"_L1;
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*!
+ \fn QDebug QRestReply::operator<<(QDebug debug, const QRestReply &reply)
+
+ Writes the \a reply into the \a debug object for debugging purposes.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QRestReply &reply)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+ if (!reply.networkReply()) {
+ debug << "QRestReply(no network reply)";
+ return debug;
+ }
+ debug << "QRestReply(isSuccess = " << reply.isSuccess()
+ << ", httpStatus = " << reply.httpStatus()
+ << ", isHttpStatusSuccess = " << reply.isHttpStatusSuccess()
+ << ", hasError = " << reply.hasError()
+ << ", errorString = " << reply.errorString()
+ << ", error = " << reply.error()
+ << ", isFinished = " << reply.networkReply()->isFinished()
+ << ", bytesAvailable = " << reply.networkReply()->bytesAvailable()
+ << ", url " << reply.networkReply()->url()
+ << ", operation = " << operationName(reply.networkReply()->operation())
+ << ", reply headers = " << reply.networkReply()->headers()
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+static constexpr auto parse_OWS(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView ows, tail;
+ };
+
+ constexpr auto is_OWS_char = [](auto ch) { return ch == ' ' || ch == '\t'; };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_OWS_char(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr void eat_OWS(QByteArrayView &data) noexcept
+{
+ data = parse_OWS(data).tail;
+}
+
+static constexpr auto parse_quoted_string(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QByteArrayView quotedString, tail;
+ constexpr explicit operator bool() const noexcept { return !quotedString.isEmpty(); }
+ };
+
+ if (!data.startsWith('"'))
+ return R{{}, data};
+
+ qsizetype i = 1; // one past initial DQUOTE
+ while (i < data.size()) {
+ switch (auto ch = data[i++]) {
+ case '"': // final DQUOTE -> end of string
+ return R{data.first(i), data.sliced(i)};
+ case '\\': // quoted-pair
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-3:
+ // Recipients that process the value of a quoted-string MUST handle a
+ // quoted-pair as if it were replaced by the octet following the backslash.
+ if (i == data.size())
+ break; // premature end
+ ch = data[i++]; // eat '\\'
+ [[fallthrough]];
+ default:
+ // we don't validate quoted-string octets to be only qdtext (Postel's Law)
+ yield(ch);
+ }
+ }
+
+ return R{{}, data}; // premature end
+}
+
+static constexpr bool is_tchar(char ch) noexcept
+{
+ // ### optimize
+ switch (ch) {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '^':
+ case '_':
+ case '`':
+ case '|':
+ case '~':
+ return true;
+ default:
+ return (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'Z');
+ }
+}
+
+static constexpr auto parse_comment(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView comment, tail;
+ constexpr explicit operator bool() const noexcept { return !comment.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, data}; // preserves original `data`
+
+ // comment = "(" *( ctext / quoted-pair / comment ) ")"
+ // ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
+
+ if (!data.startsWith('('))
+ return invalid;
+
+ qsizetype i = 1;
+ qsizetype level = 1;
+ while (i < data.size()) {
+ switch (data[i++]) {
+ case '(': // nested comment
+ ++level;
+ break;
+ case ')': // end of comment
+ if (--level == 0)
+ return R{data.first(i), data.sliced(i)};
+ break;
+ case '\\': // quoted-pair
+ if (i == data.size())
+ return invalid; // premature end
+ ++i; // eat escaped character
+ break;
+ default:
+ ; // don't validate ctext - accept everything (Postel's Law)
+ }
+ }
+
+ return invalid; // premature end / unbalanced nesting levels
+}
+
+static constexpr void eat_CWS(QByteArrayView &data) noexcept
+{
+ eat_OWS(data);
+ while (const auto comment = parse_comment(data)) {
+ data = comment.tail;
+ eat_OWS(data);
+ }
+}
+
+static constexpr auto parse_token(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView token, tail;
+ constexpr explicit operator bool() const noexcept { return !token.isEmpty(); }
+ };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_tchar(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr auto parse_parameter(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QLatin1StringView name; QByteArrayView value; QByteArrayView tail;
+ constexpr explicit operator bool() const noexcept { return !name.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, {}, data}; // preserves original `data`
+
+ // parameter = parameter-name "=" parameter-value
+ // parameter-name = token
+ // parameter-value = ( token / quoted-string )
+
+ const auto name = parse_token(data);
+ if (!name)
+ return invalid;
+ data = name.tail;
+
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('='))
+ return invalid;
+ data = data.sliced(1);
+
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (Q_UNLIKELY(data.startsWith('"'))) { // value is a quoted-string
+
+ const auto value = parse_quoted_string(data, yield);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.quotedString, data};
+
+ } else { // value is a token
+
+ const auto value = parse_token(data);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.token, data};
+ }
+}
+
+static auto parse_content_type(QByteArrayView data)
+{
+ struct R {
+ QLatin1StringView type, subtype;
+ std::string charset;
+ constexpr explicit operator bool() const noexcept { return !type.isEmpty(); }
+ };
+
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto type = parse_token(data);
+ if (!type)
+ return R{};
+ data = type.tail;
+
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('/'))
+ return R{};
+ data = data.sliced(1);
+
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto subtype = parse_token(data);
+ if (!subtype)
+ return R{};
+ data = subtype.tail;
+
+ eat_CWS(data);
+
+ auto r = R{QLatin1StringView{type.token}, QLatin1StringView{subtype.token}, {}};
+
+ while (data.startsWith(';')) {
+
+ data = data.sliced(1); // eat ';'
+
+ eat_CWS(data);
+
+ const auto param = parse_parameter(data, [&](char ch) { r.charset.append(1, ch); });
+ if (param.name.compare("charset"_L1, Qt::CaseInsensitive) == 0) {
+ if (r.charset.empty() && !param.value.startsWith('"')) // wasn't a quoted-string
+ r.charset.assign(param.value.begin(), param.value.end());
+ return r; // charset found
+ }
+ r.charset.clear(); // wasn't an actual charset
+ if (param.tail.size() == data.size()) // no progress was made
+ break; // returns {type, subtype}
+ // otherwise, continue (accepting e.g. `;;`)
+ data = param.tail;
+
+ eat_CWS(data);
+ }
+
+ return r; // no charset found
+}
+
+QByteArray QRestReplyPrivate::contentCharset(const QNetworkReply* reply)
+{
+ // Content-type consists of mimetype and optional parameters, of which one may be 'charset'
+ // Example values and their combinations below are all valid, see RFC 7231 section 3.1.1.5
+ // and RFC 2045 section 5.1
+ //
+ // text/plain; charset=utf-8
+ // text/plain; charset=utf-8;version=1.7
+ // text/plain; charset = utf-8
+ // text/plain; charset ="utf-8"
+
+ const QByteArray contentTypeValue =
+ reply->headers().value(QHttpHeaders::WellKnownHeader::ContentType).toByteArray();
+
+ const auto r = parse_content_type(contentTypeValue);
+ if (r && !r.charset.empty())
+ return QByteArrayView(r.charset).toByteArray();
+ else
+ return "UTF-8"_ba; // Default to the most commonly used UTF-8.
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qrestreply.h b/src/network/access/qrestreply.h
new file mode 100644
index 0000000000..c32fff1d4e
--- /dev/null
+++ b/src/network/access/qrestreply.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_H
+#define QRESTREPLY_H
+
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qpointer.h>
+
+#include <optional>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QDebug;
+struct QJsonParseError;
+class QJsonDocument;
+class QString;
+
+class QRestReplyPrivate;
+class QT_TECH_PREVIEW_API QRestReply
+{
+public:
+ Q_NETWORK_EXPORT explicit QRestReply(QNetworkReply *reply);
+ Q_NETWORK_EXPORT ~QRestReply();
+
+ QRestReply(QRestReply &&other) noexcept
+ : wrapped(std::move(other.wrapped)),
+ d(std::exchange(other.d, nullptr))
+ {
+ }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRestReply)
+ void swap(QRestReply &other) noexcept
+ {
+ wrapped.swap(other.wrapped);
+ qt_ptr_swap(d, other.d);
+ }
+
+ Q_NETWORK_EXPORT QNetworkReply *networkReply() const;
+
+ Q_NETWORK_EXPORT std::optional<QJsonDocument> readJson(QJsonParseError *error = nullptr);
+ Q_NETWORK_EXPORT QByteArray readBody();
+ Q_NETWORK_EXPORT QString readText();
+
+ bool isSuccess() const
+ {
+ return !hasError() && isHttpStatusSuccess();
+ }
+ Q_NETWORK_EXPORT int httpStatus() const;
+ Q_NETWORK_EXPORT bool isHttpStatusSuccess() const;
+
+ Q_NETWORK_EXPORT bool hasError() const;
+ Q_NETWORK_EXPORT QNetworkReply::NetworkError error() const;
+ Q_NETWORK_EXPORT QString errorString() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestReply &reply);
+#endif
+ QPointer<QNetworkReply> wrapped;
+ QRestReplyPrivate *d = nullptr;
+ Q_DISABLE_COPY(QRestReply)
+};
+
+Q_DECLARE_SHARED(QRestReply)
+
+QT_END_NAMESPACE
+
+#endif // QRESTREPLY_H
diff --git a/src/network/access/qrestreply_p.h b/src/network/access/qrestreply_p.h
new file mode 100644
index 0000000000..ec963cf168
--- /dev/null
+++ b/src/network/access/qrestreply_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_P_H
+#define QRESTREPLY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qstringconverter_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QNetworkReply;
+
+class QRestReplyPrivate
+{
+public:
+ QRestReplyPrivate();
+ ~QRestReplyPrivate();
+
+ std::optional<QStringDecoder> decoder;
+
+ static QByteArray contentCharset(const QNetworkReply *reply);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qsocketabstraction_p.h b/src/network/access/qsocketabstraction_p.h
new file mode 100644
index 0000000000..2b40b80244
--- /dev/null
+++ b/src/network/access/qsocketabstraction_p.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSOCKETABSTRACTION_P_H
+#define QSOCKETABSTRACTION_P_H
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qlocalsocket.h>
+
+#include <QtCore/qxpfunctional.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+// Helper functions to deal with a QIODevice that is either a socket or a local
+// socket.
+namespace QSocketAbstraction {
+template <typename Fn, typename... Args>
+auto visit(Fn &&fn, QIODevice *socket, Args &&...args)
+{
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ if (auto *s = qobject_cast<QLocalSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ Q_UNREACHABLE();
+}
+
+// Since QLocalSocket's LocalSocketState's values are defined as being equal
+// to some of QAbstractSocket's SocketState's values, we can use the superset
+// of the two as the return type.
+inline QAbstractSocket::SocketState socketState(QIODevice *device)
+{
+ auto getState = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->state();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketState st = s->state();
+ return static_cast<QAbstractSocket::SocketState>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getState, device);
+}
+
+// Same as for socketState(), but for the errors
+inline QAbstractSocket::SocketError socketError(QIODevice *device)
+{
+ auto getError = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->error();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketError st = s->error();
+ return static_cast<QAbstractSocket::SocketError>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getError, device);
+}
+
+inline QString socketPeerName(QIODevice *device)
+{
+ auto getPeerName = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->peerName();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ return s->serverName();
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getPeerName, device);
+}
+} // namespace QSocketAbstraction
+
+QT_END_NAMESPACE
+
+#endif // QSOCKETABSTRACTION_P_H
diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp
deleted file mode 100644
index eef8df288d..0000000000
--- a/src/network/access/qspdyprotocolhandler.cpp
+++ /dev/null
@@ -1,1304 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <private/qspdyprotocolhandler_p.h>
-#include <private/qnoncontiguousbytedevice_p.h>
-#include <private/qhttpnetworkconnectionchannel_p.h>
-#include <QtCore/QtEndian>
-
-#if !defined(QT_NO_SSL)
-
-QT_BEGIN_NAMESPACE
-
-static const char spdyDictionary[] = {
- 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti
- 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h
- 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p
- 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p
- 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de
- 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete....
- 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace...
- 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept.
- 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
- 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse
- 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc
- 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco
- 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding....
- 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l
- 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage.
- 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
- 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges
- 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age.
- 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow
- 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth
- 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio
- 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac
- 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr
- 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co
- 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection
- 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont
- 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base
- 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont
- 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco
- 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding....
- 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content-
- 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language
- 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont
- 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng
- 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co
- 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo
- 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation..
- 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
- 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5...
- 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content
- 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range..
- 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
- 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type..
- 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date..
- 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag..
- 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect
- 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi
- 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f
- 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h
- 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i
- 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match.
- 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo
- 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s
- 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince....
- 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none-
- 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match...
- 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang
- 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if-
- 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi
- 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since
- 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last
- 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie
- 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation...
- 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for
- 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards...
- 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma.
- 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy
- 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent
- 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate...
- 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza
- 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion....
- 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range...
- 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer
- 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr
- 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after.
- 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve
- 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te.
- 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail
- 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr
- 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e
- 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding.
- 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra
- 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us
- 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent
- 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary
- 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via.
- 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni
- 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww
- 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen
- 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate..
- 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method
- 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get.
- 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu
- 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200
- 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v
- 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion..
- 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1
- 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur
- 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub
- 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s
- 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki
- 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee
- 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive.
- 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi
- 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012
- 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205
- 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030
- 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043
- 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307
- 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540
- 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084
- 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411
- 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341
- 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164
- 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504
- 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N
- 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho
- 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative
- 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa
- 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204.
- 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte
- 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo
- 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm
- 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4
- 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40
- 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth
- 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40
- 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid
- 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N
- 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found
- 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte
- 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser
- 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro
- 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not
- 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme
- 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503.
- 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service.
- 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila
- 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F
- 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A
- 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J
- 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A
- 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept.
- 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov.
- 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0
- 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon
- 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W
- 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu.
- 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa
- 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun..
- 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk
- 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text.
- 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima
- 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i
- 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg
- 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g
- 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli
- 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
- 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli
- 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
- 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml
- 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl
- 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text
- 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr
- 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ
- 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat
- 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age
- 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de
- 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd
- 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse
- 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c
- 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i
- 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859-
- 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-..
- 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0.
-};
-
-// uncomment to debug
-//static void printHex(const QByteArray &ba)
-//{
-// QByteArray hex;
-// QByteArray clearText;
-// for (int a = 0; a < ba.count(); ++a) {
-// QByteArray currentHexChar = QByteArray(1, ba.at(a)).toHex().rightJustified(2, ' ');
-// QByteArray currentChar;
-// if (ba.at(a) >= 32 && ba.at(a) < 126) { // if ASCII, print the letter
-// currentChar = QByteArray(1, ba.at(a));
-// } else {
-// currentChar = " ";
-// }
-// clearText.append(currentChar.rightJustified(2, ' '));
-// hex.append(currentHexChar);
-// hex.append(' ');
-// clearText.append(' ');
-// }
-// int chunkSize = 102; // 12 == 4 bytes per line
-// for (int a = 0; a < hex.count(); a += chunkSize) {
-// qDebug() << hex.mid(a, chunkSize);
-// qDebug() << clearText.mid(a, chunkSize);
-// }
-//}
-
-QSpdyProtocolHandler::QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel)
- : QObject(nullptr), QAbstractProtocolHandler(channel),
- m_nextStreamID(-1),
- m_maxConcurrentStreams(100), // 100 is recommended in the SPDY RFC
- m_initialWindowSize(0),
- m_waitingForCompleteStream(false)
-{
- m_inflateStream.zalloc = Z_NULL;
- m_inflateStream.zfree = Z_NULL;
- m_inflateStream.opaque = Z_NULL;
- int zlibRet = inflateInit(&m_inflateStream);
- Q_ASSERT(zlibRet == Z_OK);
-
- m_deflateStream.zalloc = Z_NULL;
- m_deflateStream.zfree = Z_NULL;
- m_deflateStream.opaque = Z_NULL;
-
- // Do actually not compress (i.e. compression level = 0)
- // when sending the headers because of the CRIME attack
- zlibRet = deflateInit(&m_deflateStream, /* compression level = */ 0);
- Q_ASSERT(zlibRet == Z_OK);
- Q_UNUSED(zlibRet); // silence -Wunused-variable
-}
-
-QSpdyProtocolHandler::~QSpdyProtocolHandler()
-{
- deflateEnd(&m_deflateStream);
- deflateEnd(&m_inflateStream);
-}
-
-bool QSpdyProtocolHandler::sendRequest()
-{
- Q_ASSERT(!m_reply);
-
- int maxPossibleRequests = m_maxConcurrentStreams - m_inFlightStreams.count();
- Q_ASSERT(maxPossibleRequests >= 0);
- if (maxPossibleRequests == 0)
- return true; // return early if max concurrent requests are exceeded
-
- m_channel->state = QHttpNetworkConnectionChannel::WritingState;
-
- int requestsToSend = qMin(m_channel->spdyRequestsToSend.size(), maxPossibleRequests);
-
- QMultiMap<int, HttpMessagePair>::iterator it = m_channel->spdyRequestsToSend.begin();
- // requests will be ordered by priority (see QMultiMap doc)
- for (int a = 0; a < requestsToSend; ++a) {
- HttpMessagePair currentPair = *it;
- QHttpNetworkRequest currentRequest = currentPair.first;
- QHttpNetworkReply *currentReply = currentPair.second;
-
- currentReply->setSpdyWasUsed(true);
- qint32 streamID = generateNextStreamID();
- m_streamIDs.insert(currentReply, streamID);
-
- currentReply->setRequest(currentRequest);
- currentReply->d_func()->connection = m_connection;
- currentReply->d_func()->connectionChannel = m_channel;
- m_inFlightStreams.insert(streamID, currentPair);
- connect(currentReply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*)));
-
- sendSYN_STREAM(currentPair, streamID, /* associatedToStreamID = */ 0);
- m_channel->spdyRequestsToSend.erase(it++);
- }
- m_channel->state = QHttpNetworkConnectionChannel::IdleState;
- return true;
-}
-
-void QSpdyProtocolHandler::_q_replyDestroyed(QObject* reply)
-{
- qint32 streamID = m_streamIDs.take(reply);
- if (m_inFlightStreams.remove(streamID))
- sendRST_STREAM(streamID, RST_STREAM_CANCEL);
-}
-
-void QSpdyProtocolHandler::_q_receiveReply()
-{
- Q_ASSERT(m_socket);
-
- // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
- // this function is called from _q_disconnected which is called because
- // of ~QHttpNetworkConnectionPrivate
- if (!qobject_cast<QHttpNetworkConnection*>(m_connection)) {
- return;
- }
-
- if (bytesAvailable() < 8)
- return; // cannot read frame headers, wait for more data
-
- char frameHeadersRaw[8];
- if (!readNextChunk(8, frameHeadersRaw))
- return; // this should not happen, we just checked
-
- const QByteArray frameHeaders(frameHeadersRaw, 8); // ### try without memcpy
- if (frameHeadersRaw[0] & 0x80) {
- handleControlFrame(frameHeaders);
- } else {
- handleDataFrame(frameHeaders);
- }
-
- // after handling the current frame, check whether there is more data waiting
- if (m_socket->bytesAvailable() > 0)
- QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
-}
-
-void QSpdyProtocolHandler::_q_readyRead()
-{
- _q_receiveReply();
-}
-
-static qint16 twoBytesToInt(const char *bytes)
-{
- return qFromBigEndian<qint16>(bytes);
-}
-
-static qint32 threeBytesToInt(const char *bytes)
-{
- return qFromBigEndian<qint32>(bytes) >> 8;
-}
-
-static qint32 fourBytesToInt(const char *bytes)
-{
- return qFromBigEndian<qint32>(bytes);
-}
-
-static void appendIntToThreeBytes(char *output, qint32 number)
-{
- qToBigEndian<qint16>(number, output + 1);
- qToBigEndian<qint8>(number >> 16, output);
-}
-
-static void appendIntToFourBytes(char *output, qint32 number)
-{
- qToBigEndian<qint32>(number, output);
-}
-
-static QByteArray intToFourBytes(qint32 number) // ### try to use appendIntToFourBytes where possible
-{
- char data[4];
- qToBigEndian<qint32>(number, data);
- QByteArray ret(data, 4);
- return ret;
-}
-
-static QByteArray intToThreeBytes(qint32 number)
-{
- char data[4];
- qToBigEndian<qint32>(number << 8, data);
- QByteArray ret(data, 3);
- return ret;
-}
-
-static qint32 getStreamID(const char *bytes)
-{
- // eliminate most significant bit; it might be 0 or 1 depending on whether
- // we are dealing with a control or data frame
- return fourBytesToInt(bytes) & 0x3fffffff;
-}
-
-static QByteArray headerField(const QByteArray &name, const QByteArray &value)
-{
- QByteArray ret;
- ret.reserve(name.count() + value.count() + 8); // 4 byte for length each
- ret.append(intToFourBytes(name.count()));
- ret.append(name);
- ret.append(intToFourBytes(value.count()));
- ret.append(value);
- return ret;
-}
-
-bool QSpdyProtocolHandler::uncompressHeader(const QByteArray &input, QByteArray *output)
-{
- const size_t chunkSize = 1024;
- char outputRaw[chunkSize];
- // input bytes will not be changed by zlib, so it is safe to const_cast here
- m_inflateStream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(input.constData()));
- m_inflateStream.avail_in = input.count();
- m_inflateStream.total_in = input.count();
- int zlibRet;
-
- do {
- m_inflateStream.next_out = reinterpret_cast<Bytef *>(outputRaw);
- m_inflateStream.avail_out = chunkSize;
- zlibRet = inflate(&m_inflateStream, Z_SYNC_FLUSH);
- if (zlibRet == Z_NEED_DICT) {
- zlibRet = inflateSetDictionary(&m_inflateStream,
- reinterpret_cast<const Bytef*>(spdyDictionary),
- /* dictionaryLength = */ 1423);
- Q_ASSERT(zlibRet == Z_OK);
- continue;
- }
- switch (zlibRet) {
- case Z_BUF_ERROR: {
- if (m_inflateStream.avail_in == 0) {
- int outputSize = chunkSize - m_inflateStream.avail_out;
- output->append(outputRaw, outputSize);
- m_inflateStream.avail_out = chunkSize;
- }
- break;
- }
- case Z_OK: {
- int outputSize = chunkSize - m_inflateStream.avail_out;
- output->append(outputRaw, outputSize);
- break;
- }
- default: {
- qWarning("got unexpected zlib return value: %d", zlibRet);
- return false;
- }
- }
- } while (m_inflateStream.avail_in > 0 && zlibRet != Z_STREAM_END);
-
- Q_ASSERT(m_inflateStream.avail_in == 0);
- return true;
-}
-
-QByteArray QSpdyProtocolHandler::composeHeader(const QHttpNetworkRequest &request)
-{
- QByteArray uncompressedHeader;
- uncompressedHeader.reserve(300); // rough estimate
-
- // calculate additional headers first, because we need to know the size
- // ### do not partially copy the list, but restrict the set header fields
- // in QHttpNetworkConnection
- QVector<QPair<QByteArray, QByteArray> > additionalHeaders;
- for (int a = 0; a < request.header().count(); ++a) {
- QByteArray key = request.header().at(a).first;
- if (key == "Connection" || key == "Host" || key == "Keep-Alive"
- || key == "Proxy-Connection" || key == "Transfer-Encoding")
- continue; // those headers are not valid (section 3.2.1)
- additionalHeaders.append(request.header().at(a));
- }
-
- qint32 numberOfHeaderPairs = 5 + additionalHeaders.count(); // 5 mandatory below + the additional ones
- uncompressedHeader.append(intToFourBytes(numberOfHeaderPairs));
-
- // mandatory header fields:
-
- uncompressedHeader.append(headerField(":method", request.methodName()));
-#ifndef QT_NO_NETWORKPROXY
- bool useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
- uncompressedHeader.append(headerField(":path", request.uri(useProxy)));
-#else
- uncompressedHeader.append(headerField(":path", request.uri(false)));
-#endif
- uncompressedHeader.append(headerField(":version", "HTTP/1.1"));
-
- uncompressedHeader.append(headerField(":host", request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1()));
-
- uncompressedHeader.append(headerField(":scheme", request.url().scheme().toLatin1()));
-
- // end of mandatory header fields
-
- // now add the additional headers
- for (int a = 0; a < additionalHeaders.count(); ++a) {
- uncompressedHeader.append(headerField(additionalHeaders.at(a).first.toLower(),
- additionalHeaders.at(a).second));
- }
-
- m_deflateStream.total_in = uncompressedHeader.count();
- m_deflateStream.avail_in = uncompressedHeader.count();
- m_deflateStream.next_in = reinterpret_cast<unsigned char *>(uncompressedHeader.data());
- int outputBytes = uncompressedHeader.count() + 30; // 30 bytes of compression header overhead
- m_deflateStream.avail_out = outputBytes;
- unsigned char *out = new unsigned char[outputBytes];
- m_deflateStream.next_out = out;
- int availOutBefore = m_deflateStream.avail_out;
- int zlibRet = deflate(&m_deflateStream, Z_SYNC_FLUSH); // do everything in one go since we use no compression
- int compressedHeaderSize = availOutBefore - m_deflateStream.avail_out;
- Q_ASSERT(zlibRet == Z_OK); // otherwise, we need to allocate more outputBytes
- Q_UNUSED(zlibRet); // silence -Wunused-variable
- Q_ASSERT(m_deflateStream.avail_in == 0);
- QByteArray compressedHeader(reinterpret_cast<char *>(out), compressedHeaderSize);
- delete[] out;
-
- return compressedHeader;
-}
-
-quint64 QSpdyProtocolHandler::bytesAvailable() const
-{
- Q_ASSERT(m_socket);
- return m_spdyBuffer.byteAmount() + m_socket->bytesAvailable();
-}
-
-bool QSpdyProtocolHandler::readNextChunk(qint64 length, char *sink)
-{
- qint64 expectedReadBytes = length;
- qint64 requiredBytesFromBuffer = 0;
-
- if (m_waitingForCompleteStream) {
- requiredBytesFromBuffer = qMin(length, m_spdyBuffer.byteAmount());
- // ### if next chunk from buffer bigger than what we want to read,
- // we have to call read() (which memcpy's). Otherwise, we can just
- // read the next chunk without memcpy'ing.
- qint64 bytesReadFromBuffer = m_spdyBuffer.read(sink, requiredBytesFromBuffer);
- Q_ASSERT(bytesReadFromBuffer == requiredBytesFromBuffer);
- if (length <= bytesReadFromBuffer) {
- return true; // buffer > required size -> no need to read from socket
- }
- expectedReadBytes -= requiredBytesFromBuffer;
- }
- qint64 readBytes = m_socket->read(sink + requiredBytesFromBuffer, expectedReadBytes);
-
- if (readBytes < expectedReadBytes) {
- m_waitingForCompleteStream = true;
- // ### this is inefficient, we should not put back so much data into the buffer
- QByteArray temp(sink, requiredBytesFromBuffer + readBytes);
- m_spdyBuffer.append(temp);
- return false;
- } else {
- return true; // buffer must be cleared by calling function
- }
-}
-
-void QSpdyProtocolHandler::sendControlFrame(FrameType type,
- ControlFrameFlags flags,
- const char *data,
- quint32 length)
-{
- // frame type and stream ID
- char header[8];
- header[0] = 0x80u; // leftmost bit == 1 -> is a control frame
- header[1] = 0x03; // 3 bit == version 3
- header[2] = 0;
- switch (type) {
- case FrameType_CREDENTIAL: {
- qWarning("sending SPDY CREDENTIAL frame is not yet implemented"); // QTBUG-36188
- return;
- }
- default:
- header[3] = type;
- }
-
- // flags
- header[4] = 0;
- if (flags & ControlFrame_FLAG_FIN || length == 0) {
- Q_ASSERT(type == FrameType_SYN_STREAM || type == FrameType_SYN_REPLY
- || type == FrameType_HEADERS || length == 0);
- header[4] |= ControlFrame_FLAG_FIN;
- }
- if (flags & ControlFrame_FLAG_UNIDIRECTIONAL) {
- Q_ASSERT(type == FrameType_SYN_STREAM);
- header[4] |= ControlFrame_FLAG_UNIDIRECTIONAL;
- }
-
- // length
- appendIntToThreeBytes(header + 5, length);
-
- qint64 written = m_socket->write(header, 8);
- Q_ASSERT(written == 8);
- written = m_socket->write(data, length);
- Q_ASSERT(written == length);
- Q_UNUSED(written); // silence -Wunused-variable
-}
-
-void QSpdyProtocolHandler::sendSYN_STREAM(const HttpMessagePair &messagePair,
- qint32 streamID, qint32 associatedToStreamID)
-{
- QHttpNetworkRequest request = messagePair.first;
- QHttpNetworkReply *reply = messagePair.second;
-
- ControlFrameFlags flags;
-
- if (!request.uploadByteDevice()) {
- // no upload -> this is the last frame, send the FIN flag
- flags |= ControlFrame_FLAG_FIN;
- reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYHalfClosed;
- } else {
- reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYUploading;
-
- // hack: set the stream ID on the device directly, so when we get
- // the signal for uploading we know which stream we are sending on
- m_streamIDs.insert(request.uploadByteDevice(), streamID);
-
- QObject::connect(request.uploadByteDevice(), SIGNAL(readyRead()), this,
- SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
- QObject::connect(request.uploadByteDevice(), SIGNAL(destroyed(QObject*)), this,
- SLOT(_q_uploadDataDestroyed(QObject *)));
- }
-
- QByteArray namesAndValues = composeHeader(request);
- quint32 length = namesAndValues.count() + 10; // 10 == 4 for Stream-ID + 4 for Associated-To-Stream-ID
- // + 2 for Priority, Unused and Slot
-
- QByteArray wireData;
- wireData.reserve(length);
- wireData.append(intToFourBytes(streamID));
- wireData.append(intToFourBytes(associatedToStreamID));
-
- // priority (3 bits) / unused (5 bits) / slot (8 bits)
- char prioAndSlot[2];
- switch (request.priority()) {
- case QHttpNetworkRequest::HighPriority:
- prioAndSlot[0] = 0x00; // == prio 0 (highest)
- break;
- case QHttpNetworkRequest::NormalPriority:
- prioAndSlot[0] = 0x80u; // == prio 4
- break;
- case QHttpNetworkRequest::LowPriority:
- prioAndSlot[0] = 0xe0u; // == prio 7 (lowest)
- break;
- }
- prioAndSlot[1] = 0x00; // slot in client certificates (not supported currently)
- wireData.append(prioAndSlot, 2);
-
- wireData.append(namesAndValues);
-
- sendControlFrame(FrameType_SYN_STREAM, flags, wireData.constData(), length);
-
- if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYUploading)
- uploadData(streamID);
-}
-
-void QSpdyProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData)
-{
- m_streamIDs.remove(uploadData);
-}
-
-void QSpdyProtocolHandler::sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode)
-{
- char wireData[8];
- appendIntToFourBytes(wireData, streamID);
- appendIntToFourBytes(wireData + 4, statusCode);
- sendControlFrame(FrameType_RST_STREAM, /* flags = */ { }, wireData, /* length = */ 8);
-}
-
-void QSpdyProtocolHandler::sendPING(quint32 pingID)
-{
- char rawData[4];
- appendIntToFourBytes(rawData, pingID);
- sendControlFrame(FrameType_PING, /* flags = */ { }, rawData, /* length = */ 4);
-}
-
-bool QSpdyProtocolHandler::uploadData(qint32 streamID)
-{
- // we only rely on SPDY flow control here and don't care about TCP buffers
- if (!m_inFlightStreams.contains(streamID)) {
- sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
- return false;
- }
-
- HttpMessagePair messagePair = m_inFlightStreams.value(streamID);
- QHttpNetworkRequest request = messagePair.first;
- QHttpNetworkReply *reply = messagePair.second;
- Q_ASSERT(reply);
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- Q_ASSERT(replyPrivate);
-
- if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) {
- qWarning("Trying to upload to closed stream");
- return false;
- }
-
- qint32 dataLeftInWindow = replyPrivate->windowSizeUpload
- - replyPrivate->currentlyUploadedDataInWindow;
-
- while (dataLeftInWindow > 0 && !request.uploadByteDevice()->atEnd()) {
-
- // get pointer to upload data
- qint64 currentReadSize = 0;
- const char *readPointer = request.uploadByteDevice()->readPointer(dataLeftInWindow,
- currentReadSize);
-
- if (currentReadSize == -1) {
- // premature eof happened
- m_connection->d_func()->emitReplyError(m_socket, reply,
- QNetworkReply::UnknownNetworkError);
- return false;
- } else if (readPointer == nullptr || currentReadSize == 0) {
- // nothing to read currently, break the loop
- break;
- } else {
- DataFrameFlags flags;
- // we will send the FIN flag later if appropriate
- qint64 currentWriteSize = sendDataFrame(streamID, flags, currentReadSize, readPointer);
- if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
- // socket broke down
- m_connection->d_func()->emitReplyError(m_socket, reply,
- QNetworkReply::UnknownNetworkError);
- return false;
- } else {
- replyPrivate->currentlyUploadedDataInWindow += currentWriteSize;
- replyPrivate->totallyUploadedData += currentWriteSize;
- dataLeftInWindow = replyPrivate->windowSizeUpload
- - replyPrivate->currentlyUploadedDataInWindow;
- request.uploadByteDevice()->advanceReadPointer(currentWriteSize);
-
- emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
- request.contentLength());
- }
- }
- }
- if (replyPrivate->totallyUploadedData == request.contentLength()) {
- DataFrameFlags finFlag = DataFrame_FLAG_FIN;
- qint64 writeSize = sendDataFrame(streamID, finFlag, 0, nullptr);
- Q_ASSERT(writeSize == 0);
- Q_UNUSED(writeSize); // silence -Wunused-variable
- replyPrivate->state = QHttpNetworkReplyPrivate::SPDYHalfClosed;
- if (reply->request().uploadByteDevice())
- reply->request().uploadByteDevice()->disconnect(this);
- // ### this will not work if the content length is not known, but
- // then again many servers will fail in this case anyhow according
- // to the SPDY RFC
- }
- return true;
-}
-
-void QSpdyProtocolHandler::_q_uploadDataReadyRead()
-{
- QNonContiguousByteDevice *device = qobject_cast<QNonContiguousByteDevice *>(sender());
- Q_ASSERT(device);
- qint32 streamID = m_streamIDs.value(device);
- Q_ASSERT(streamID > 0);
- uploadData(streamID);
-}
-
-void QSpdyProtocolHandler::sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize)
-{
- char windowUpdateData[8];
- appendIntToFourBytes(windowUpdateData, streamID);
- appendIntToFourBytes(windowUpdateData + 4, deltaWindowSize);
-
- sendControlFrame(FrameType_WINDOW_UPDATE, /* flags = */ { }, windowUpdateData, /* length = */ 8);
-}
-
-qint64 QSpdyProtocolHandler::sendDataFrame(qint32 streamID, DataFrameFlags flags,
- quint32 length, const char *data)
-{
- QByteArray wireData;
- wireData.reserve(8);
-
- wireData.append(intToFourBytes(streamID));
- wireData.append(flags);
- wireData.append(intToThreeBytes(length));
-
- Q_ASSERT(m_socket);
- m_socket->write(wireData);
-
- if (data) {
- qint64 ret = m_socket->write(data, length);
- return ret;
- } else {
- return 0; // nothing to write, e.g. FIN flag
- }
-}
-
-void QSpdyProtocolHandler::handleControlFrame(const QByteArray &frameHeaders) // ### make it char *
-{
- Q_ASSERT(frameHeaders.count() >= 8);
- qint16 version = twoBytesToInt(frameHeaders.constData());
- version &= 0x3fff; // eliminate most significant bit to determine version
- Q_ASSERT(version == 3);
-
- qint16 type = twoBytesToInt(frameHeaders.constData() + 2);
-
- char flags = frameHeaders.at(4);
- qint32 length = threeBytesToInt(frameHeaders.constData() + 5);
- Q_ASSERT(length > 0);
-
- QByteArray frameData;
- frameData.resize(length);
- if (!readNextChunk(length, frameData.data())) {
- // put back the frame headers to the buffer
- m_spdyBuffer.prepend(frameHeaders);
- return; // we couldn't read the whole frame and need to wait
- } else {
- m_spdyBuffer.clear();
- m_waitingForCompleteStream = false;
- }
-
- switch (type) {
- case FrameType_SYN_STREAM: {
- handleSYN_STREAM(flags, length, frameData);
- break;
- }
- case FrameType_SYN_REPLY: {
- handleSYN_REPLY(flags, length, frameData);
- break;
- }
- case FrameType_RST_STREAM: {
- handleRST_STREAM(flags, length, frameData);
- break;
- }
- case FrameType_SETTINGS: {
- handleSETTINGS(flags, length, frameData);
- break;
- }
- case FrameType_PING: {
- handlePING(flags, length, frameData);
- break;
- }
- case FrameType_GOAWAY: {
- handleGOAWAY(flags, length, frameData);
- break;
- }
- case FrameType_HEADERS: {
- handleHEADERS(flags, length, frameData);
- break;
- }
- case FrameType_WINDOW_UPDATE: {
- handleWINDOW_UPDATE(flags, length, frameData);
- break;
- }
- default:
- qWarning("cannot handle frame of type %d", int(type));
- }
-}
-
-void QSpdyProtocolHandler::handleSYN_STREAM(char /*flags*/, quint32 /*length*/,
- const QByteArray &frameData)
-{
- // not implemented; will be implemented when servers start using it
- // we just tell the server that we do not accept that
-
- qint32 streamID = getStreamID(frameData.constData());
-
- sendRST_STREAM(streamID, RST_STREAM_REFUSED_STREAM);
-}
-
-void QSpdyProtocolHandler::handleSYN_REPLY(char flags, quint32 /*length*/, const QByteArray &frameData)
-{
- parseHttpHeaders(flags, frameData);
-}
-
-void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameData)
-{
- qint32 streamID = getStreamID(frameData.constData());
- const auto it = m_inFlightStreams.constFind(streamID);
- if (it == m_inFlightStreams.cend()) {
- sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
- return;
- }
-
- flags &= 0x3f;
- bool flag_fin = flags & 0x01;
-
- QByteArray headerValuePairs = frameData.mid(4);
-
- HttpMessagePair pair = it.value();
- QHttpNetworkReply *httpReply = pair.second;
- Q_ASSERT(httpReply != nullptr);
-
- if (httpReply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) {
- sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED);
- return;
- }
-
- QByteArray uncompressedHeader;
- if (!uncompressHeader(headerValuePairs, &uncompressedHeader)) {
- qWarning("error reading header from SYN_REPLY message");
- return;
- }
-
- qint32 headerCount = fourBytesToInt(uncompressedHeader.constData());
- if (headerCount * 8 > uncompressedHeader.size()) {
- qWarning("error parsing header from SYN_REPLY message");
- sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
- return;
- }
- qint32 readPointer = 4;
- for (qint32 a = 0; a < headerCount; ++a) {
- qint32 count = fourBytesToInt(uncompressedHeader.constData() + readPointer);
- readPointer += 4;
- QByteArray name = uncompressedHeader.mid(readPointer, count);
- readPointer += count;
- if (readPointer > uncompressedHeader.size()) {
- qWarning("error parsing header from SYN_REPLY message");
- sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
- return;
- }
- count = fourBytesToInt(uncompressedHeader.constData() + readPointer);
- readPointer += 4;
- QByteArray value = uncompressedHeader.mid(readPointer, count);
- readPointer += count;
- if (readPointer > uncompressedHeader.size()) {
- qWarning("error parsing header from SYN_REPLY message");
- sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR);
- return;
- }
- if (name == ":status") {
- httpReply->setStatusCode(value.left(3).toInt());
- httpReply->d_func()->reasonPhrase = QString::fromLatin1(value.mid(4));
- } else if (name == ":version") {
- int majorVersion = value.at(5) - 48;
- int minorVersion = value.at(7) - 48;
- httpReply->d_func()->majorVersion = majorVersion;
- httpReply->d_func()->minorVersion = minorVersion;
- } else if (name == "content-length") {
- httpReply->setContentLength(value.toLongLong());
- } else {
- value.replace('\0', name == "set-cookie" ? "\n" : ", ");
- httpReply->setHeaderField(name, value);
- }
- }
- emit httpReply->headerChanged();
-
- if (flag_fin) {
- if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed)
- sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, nullptr);
- replyFinished(httpReply, streamID);
- }
-}
-
-void QSpdyProtocolHandler::handleRST_STREAM(char /*flags*/, quint32 length,
- const QByteArray &frameData)
-{
- // flags are ignored
-
- Q_ASSERT(length == 8);
- Q_UNUSED(length); // silence -Wunused-parameter
- qint32 streamID = getStreamID(frameData.constData());
- QHttpNetworkReply *httpReply = m_inFlightStreams.value(streamID).second;
-
- qint32 statusCodeInt = fourBytesToInt(frameData.constData() + 4);
- RST_STREAM_STATUS_CODE statusCode = static_cast<RST_STREAM_STATUS_CODE>(statusCodeInt);
- QNetworkReply::NetworkError errorCode;
- QByteArray errorMessage;
-
- switch (statusCode) {
- case RST_STREAM_PROTOCOL_ERROR:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "SPDY protocol error";
- break;
- case RST_STREAM_INVALID_STREAM:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "SPDY stream is not active";
- break;
- case RST_STREAM_REFUSED_STREAM:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "SPDY stream was refused";
- break;
- case RST_STREAM_UNSUPPORTED_VERSION:
- errorCode = QNetworkReply::ProtocolUnknownError;
- errorMessage = "SPDY version is unknown to the server";
- break;
- case RST_STREAM_CANCEL:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "SPDY stream is no longer needed";
- break;
- case RST_STREAM_INTERNAL_ERROR:
- errorCode = QNetworkReply::InternalServerError;
- errorMessage = "Internal server error";
- break;
- case RST_STREAM_FLOW_CONTROL_ERROR:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "peer violated the flow control protocol";
- break;
- case RST_STREAM_STREAM_IN_USE:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "server received a SYN_REPLY for an already open stream";
- break;
- case RST_STREAM_STREAM_ALREADY_CLOSED:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "server received data or a SYN_REPLY for an already half-closed stream";
- break;
- case RST_STREAM_INVALID_CREDENTIALS:
- errorCode = QNetworkReply::ContentAccessDenied;
- errorMessage = "server received invalid credentials";
- break;
- case RST_STREAM_FRAME_TOO_LARGE:
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "server cannot process the frame because it is too large";
- break;
- default:
- qWarning("could not understand servers RST_STREAM status code");
- errorCode = QNetworkReply::ProtocolFailure;
- errorMessage = "got SPDY RST_STREAM message with unknown error code";
- }
- if (httpReply)
- replyFinishedWithError(httpReply, streamID, errorCode, errorMessage.constData());
-}
-
-void QSpdyProtocolHandler::handleSETTINGS(char flags, quint32 /*length*/, const QByteArray &frameData)
-{
- Q_ASSERT(frameData.count() > 0);
-
- SETTINGS_Flags settingsFlags = static_cast<SETTINGS_Flags>(flags);
- if (settingsFlags & FLAG_SETTINGS_CLEAR_SETTINGS) {
- // ### clear all persistent settings; since we do not persist settings
- // as of now, we don't need to clear anything either
- }
-
- qint32 numberOfEntries = fourBytesToInt(frameData.constData());
- Q_ASSERT(numberOfEntries > 0);
- for (int a = 0, frameDataIndex = 4; a < numberOfEntries; ++a, frameDataIndex += 8) {
- SETTINGS_ID_Flag idFlag = static_cast<SETTINGS_ID_Flag>(frameData[frameDataIndex]);
- if (idFlag & FLAG_SETTINGS_PERSIST_VALUE) {
- // ### we SHOULD persist the settings here according to the RFC, but we don't have to,
- // so implement that later
- } // the other value is only sent by us, but not received
-
- quint32 uniqueID = static_cast<SETTINGS_ID>(
- threeBytesToInt(frameData.constData() + frameDataIndex + 1));
- quint32 value = fourBytesToInt(frameData.constData() + frameDataIndex + 4);
- switch (uniqueID) {
- case SETTINGS_UPLOAD_BANDWIDTH: {
- // ignored for now, just an estimated informative value
- break;
- }
- case SETTINGS_DOWNLOAD_BANDWIDTH: {
- // ignored for now, just an estimated informative value
- break;
- }
- case SETTINGS_ROUND_TRIP_TIME: {
- // ignored for now, just an estimated informative value
- break;
- }
- case SETTINGS_MAX_CONCURRENT_STREAMS: {
- m_maxConcurrentStreams = value;
- break;
- }
- case SETTINGS_CURRENT_CWND: {
- // ignored for now, just an informative value
- break;
- }
- case SETTINGS_DOWNLOAD_RETRANS_RATE: {
- // ignored for now, just an estimated informative value
- break;
- }
- case SETTINGS_INITIAL_WINDOW_SIZE: {
- m_initialWindowSize = value;
- break;
- }
- case SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: {
- // client certificates are not supported
- break;
- }
- default:
- qWarning("found unknown settings value %u", uint(value));
- }
- }
-}
-
-void QSpdyProtocolHandler::handlePING(char /*flags*/, quint32 length, const QByteArray &frameData)
-{
- // flags are ignored
-
- Q_ASSERT(length == 4);
- Q_UNUSED(length); // silence -Wunused-parameter
- quint32 pingID = fourBytesToInt(frameData.constData());
-
- // odd numbered IDs must be ignored
- if ((pingID & 1) == 0) // is even?
- sendPING(pingID);
-}
-
-void QSpdyProtocolHandler::handleGOAWAY(char /*flags*/, quint32 /*length*/,
- const QByteArray &frameData)
-{
- // flags are ignored
-
- qint32 statusCode = static_cast<GOAWAY_STATUS>(fourBytesToInt(frameData.constData() + 4));
- QNetworkReply::NetworkError errorCode;
- switch (statusCode) {
- case GOAWAY_OK: {
- errorCode = QNetworkReply::NoError;
- break;
- }
- case GOAWAY_PROTOCOL_ERROR: {
- errorCode = QNetworkReply::ProtocolFailure;
- break;
- }
- case GOAWAY_INTERNAL_ERROR: {
- errorCode = QNetworkReply::InternalServerError;
- break;
- }
- default:
- qWarning("unexpected status code %d", int(statusCode));
- errorCode = QNetworkReply::ProtocolUnknownError;
- }
-
- qint32 lastGoodStreamID = getStreamID(frameData.constData());
-
- // emit errors for all replies after the last good stream ID
- Q_ASSERT(m_connection);
- for (qint32 currentStreamID = lastGoodStreamID + 2; currentStreamID <= m_nextStreamID;
- ++currentStreamID) {
- QHttpNetworkReply *reply = m_inFlightStreams.value(currentStreamID).second;
- Q_ASSERT(reply);
- m_connection->d_func()->emitReplyError(m_socket, reply, errorCode);
- }
- // ### we could make sure a new session is initiated anyhow
-}
-
-void QSpdyProtocolHandler::handleHEADERS(char flags, quint32 /*length*/,
- const QByteArray &frameData)
-{
- parseHttpHeaders(flags, frameData);
-}
-
-void QSpdyProtocolHandler::handleWINDOW_UPDATE(char /*flags*/, quint32 /*length*/,
- const QByteArray &frameData)
-{
- qint32 streamID = getStreamID(frameData.constData());
- qint32 deltaWindowSize = fourBytesToInt(frameData.constData() + 4);
-
- const auto it = m_inFlightStreams.constFind(streamID);
- if (it == m_inFlightStreams.cend()) {
- sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
- return;
- }
-
- QHttpNetworkReply *reply = it.value().second;
- Q_ASSERT(reply);
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- Q_ASSERT(replyPrivate);
-
- // Ignore WINDOW_UPDATE if we are already done.
- if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed)
- return;
-
- replyPrivate->currentlyUploadedDataInWindow = replyPrivate->windowSizeUpload - deltaWindowSize;
- uploadData(streamID); // we hopefully can continue to upload
-}
-
-
-void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders)
-{
- Q_ASSERT(frameHeaders.count() >= 8);
-
- qint32 streamID = getStreamID(frameHeaders.constData());
- const auto it = m_inFlightStreams.constFind(streamID);
- if (it == m_inFlightStreams.cend()) {
- sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM);
- return;
- }
-
- unsigned char flags = static_cast<unsigned char>(frameHeaders.at(4));
- flags &= 0x3f;
- bool flag_fin = flags & 0x01;
- bool flag_compress = flags & 0x02;
- qint32 length = threeBytesToInt(frameHeaders.constData() + 5);
-
- QByteArray data;
- data.resize(length);
- if (!readNextChunk(length, data.data())) {
- // put back the frame headers to the buffer
- m_spdyBuffer.prepend(frameHeaders);
- return; // we couldn't read the whole frame and need to wait
- } else {
- m_spdyBuffer.clear();
- m_waitingForCompleteStream = false;
- }
-
- HttpMessagePair pair = it.value();
- QHttpNetworkRequest httpRequest = pair.first;
- QHttpNetworkReply *httpReply = pair.second;
- Q_ASSERT(httpReply != nullptr);
-
- QHttpNetworkReplyPrivate *replyPrivate = httpReply->d_func();
-
- if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) {
- sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED);
- return;
- }
-
- // check whether we need to send WINDOW_UPDATE (i.e. tell the sender it can send more)
- replyPrivate->currentlyReceivedDataInWindow += length;
- qint32 dataLeftInWindow = replyPrivate->windowSizeDownload - replyPrivate->currentlyReceivedDataInWindow;
-
- if (replyPrivate->currentlyReceivedDataInWindow > 0
- && dataLeftInWindow < replyPrivate->windowSizeDownload / 2) {
-
- // socket read buffer size is 64K actually, hard coded in the channel
- // We can read way more than 64K per socket, because the window size
- // here is per stream.
- if (replyPrivate->windowSizeDownload >= m_socket->readBufferSize()) {
- replyPrivate->windowSizeDownload = m_socket->readBufferSize();
- } else {
- replyPrivate->windowSizeDownload *= 1.5;
- }
- QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
- Q_ARG(qint32, streamID),
- Q_ARG(quint32, replyPrivate->windowSizeDownload));
- // setting the current data count to 0 is a race condition,
- // because we call sendWINDOW_UPDATE through the event loop.
- // But then again, the whole situation is a race condition because
- // we don't know when the packet will arrive at the server; so
- // this is most likely good enough here.
- replyPrivate->currentlyReceivedDataInWindow = 0;
- }
-
- httpReply->d_func()->compressedData.append(data);
-
-
- replyPrivate->totalProgress += length;
-
- if (httpRequest.d->autoDecompress && httpReply->d_func()->isCompressed()) {
- QByteDataBuffer inDataBuffer; // ### should we introduce one in the http reply?
- inDataBuffer.append(data);
- qint64 compressedCount = httpReply->d_func()->uncompressBodyData(&inDataBuffer,
- &replyPrivate->responseData);
- Q_ASSERT(compressedCount >= 0);
- Q_UNUSED(compressedCount); // silence -Wunused-variable
- } else {
- replyPrivate->responseData.append(data);
- }
-
- if (replyPrivate->shouldEmitSignals()) {
- emit httpReply->readyRead();
- emit httpReply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- }
-
- if (flag_compress) {
- qWarning("SPDY level compression is not supported");
- }
-
- if (flag_fin) {
- if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed)
- sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, nullptr);
- replyFinished(httpReply, streamID);
- }
-}
-
-void QSpdyProtocolHandler::replyFinished(QHttpNetworkReply *httpReply, qint32 streamID)
-{
- httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed;
- httpReply->disconnect(this);
- if (httpReply->request().uploadByteDevice())
- httpReply->request().uploadByteDevice()->disconnect(this);
- int streamsRemoved = m_inFlightStreams.remove(streamID);
- Q_ASSERT(streamsRemoved == 1);
- Q_UNUSED(streamsRemoved); // silence -Wunused-variable
- emit httpReply->finished();
-}
-
-void QSpdyProtocolHandler::replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID,
- QNetworkReply::NetworkError errorCode, const char *errorMessage)
-{
- Q_ASSERT(httpReply);
- httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed;
- httpReply->disconnect(this);
- if (httpReply->request().uploadByteDevice())
- httpReply->request().uploadByteDevice()->disconnect(this);
- int streamsRemoved = m_inFlightStreams.remove(streamID);
- Q_ASSERT(streamsRemoved == 1);
- Q_UNUSED(streamsRemoved); // silence -Wunused-variable
- emit httpReply->finishedWithError(errorCode, QSpdyProtocolHandler::tr(errorMessage));
-}
-
-qint32 QSpdyProtocolHandler::generateNextStreamID()
-{
- // stream IDs initiated by the client must be odd
- m_nextStreamID += 2;
- return m_nextStreamID;
-}
-
-QT_END_NAMESPACE
-
-#endif // !defined(QT_NO_SSL)
diff --git a/src/network/access/qspdyprotocolhandler_p.h b/src/network/access/qspdyprotocolhandler_p.h
deleted file mode 100644
index 14e2ff388a..0000000000
--- a/src/network/access/qspdyprotocolhandler_p.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSPDYPROTOCOLHANDLER_H
-#define QSPDYPROTOCOLHANDLER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the Network Access API. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <private/qabstractprotocolhandler_p.h>
-#include <QtNetwork/qnetworkreply.h>
-#include <private/qbytedata_p.h>
-
-#include <zlib.h>
-
-QT_REQUIRE_CONFIG(http);
-
-#if !defined(QT_NO_SSL)
-
-QT_BEGIN_NAMESPACE
-
-class QHttpNetworkRequest;
-
-#ifndef HttpMessagePair
-typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
-#endif
-
-class QSpdyProtocolHandler : public QObject, public QAbstractProtocolHandler {
- Q_OBJECT
-public:
- QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel);
- ~QSpdyProtocolHandler();
-
- enum DataFrameFlag {
- DataFrame_FLAG_FIN = 0x01,
- DataFrame_FLAG_COMPRESS = 0x02
- };
-
- Q_DECLARE_FLAGS(DataFrameFlags, DataFrameFlag)
-
- enum ControlFrameFlag {
- ControlFrame_FLAG_FIN = 0x01,
- ControlFrame_FLAG_UNIDIRECTIONAL = 0x02
- };
-
- Q_DECLARE_FLAGS(ControlFrameFlags, ControlFrameFlag)
-
- enum SETTINGS_Flag {
- FLAG_SETTINGS_CLEAR_SETTINGS = 0x01
- };
-
- Q_DECLARE_FLAGS(SETTINGS_Flags, SETTINGS_Flag)
-
- enum SETTINGS_ID_Flag {
- FLAG_SETTINGS_PERSIST_VALUE = 0x01,
- FLAG_SETTINGS_PERSISTED = 0x02
- };
-
- Q_DECLARE_FLAGS(SETTINGS_ID_Flags, SETTINGS_ID_Flag)
-
- virtual void _q_receiveReply() override;
- virtual void _q_readyRead() override;
- virtual bool sendRequest() override;
-
-private slots:
- void _q_uploadDataReadyRead();
- void _q_replyDestroyed(QObject*);
- void _q_uploadDataDestroyed(QObject *);
-
-private:
-
- enum FrameType {
- FrameType_SYN_STREAM = 1,
- FrameType_SYN_REPLY = 2,
- FrameType_RST_STREAM = 3,
- FrameType_SETTINGS = 4,
- FrameType_PING = 6,
- FrameType_GOAWAY = 7,
- FrameType_HEADERS = 8,
- FrameType_WINDOW_UPDATE = 9,
- FrameType_CREDENTIAL // has a special type
- };
-
- enum StatusCode {
- StatusCode_PROTOCOL_ERROR = 1,
- StatusCode_INVALID_STREAM = 2,
- StatusCode_REFUSED_STREAM = 3,
- StatusCode_UNSUPPORTED_VERSION = 4,
- StatusCode_CANCEL = 5,
- StatusCode_INTERNAL_ERROR = 6,
- StatusCode_FLOW_CONTROL_ERROR = 7,
- StatusCode_STREAM_IN_USE = 8,
- StatusCode_STREAM_ALREADY_CLOSED = 9,
- StatusCode_INVALID_CREDENTIALS = 10,
- StatusCode_FRAME_TOO_LARGE = 11
- };
-
- enum SETTINGS_ID {
- SETTINGS_UPLOAD_BANDWIDTH = 1,
- SETTINGS_DOWNLOAD_BANDWIDTH = 2,
- SETTINGS_ROUND_TRIP_TIME = 3,
- SETTINGS_MAX_CONCURRENT_STREAMS = 4,
- SETTINGS_CURRENT_CWND = 5,
- SETTINGS_DOWNLOAD_RETRANS_RATE = 6,
- SETTINGS_INITIAL_WINDOW_SIZE = 7,
- SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
- };
-
- enum GOAWAY_STATUS {
- GOAWAY_OK = 0,
- GOAWAY_PROTOCOL_ERROR = 1,
- GOAWAY_INTERNAL_ERROR = 11
- };
-
- enum RST_STREAM_STATUS_CODE {
- RST_STREAM_PROTOCOL_ERROR = 1,
- RST_STREAM_INVALID_STREAM = 2,
- RST_STREAM_REFUSED_STREAM = 3,
- RST_STREAM_UNSUPPORTED_VERSION = 4,
- RST_STREAM_CANCEL = 5,
- RST_STREAM_INTERNAL_ERROR = 6,
- RST_STREAM_FLOW_CONTROL_ERROR = 7,
- RST_STREAM_STREAM_IN_USE = 8,
- RST_STREAM_STREAM_ALREADY_CLOSED = 9,
- RST_STREAM_INVALID_CREDENTIALS = 10,
- RST_STREAM_FRAME_TOO_LARGE = 11
- };
-
- quint64 bytesAvailable() const;
- bool readNextChunk(qint64 length, char *sink);
-
- void sendControlFrame(FrameType type, ControlFrameFlags flags, const char *data, quint32 length);
-
- void sendSYN_STREAM(const HttpMessagePair &pair, qint32 streamID,
- qint32 associatedToStreamID);
- void sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode);
- void sendPING(quint32 pingID);
-
- bool uploadData(qint32 streamID);
- Q_INVOKABLE void sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize);
-
- qint64 sendDataFrame(qint32 streamID, DataFrameFlags flags, quint32 length,
- const char *data);
-
- QByteArray composeHeader(const QHttpNetworkRequest &request);
- bool uncompressHeader(const QByteArray &input, QByteArray *output);
-
- void handleControlFrame(const QByteArray &frameHeaders);
- void handleDataFrame(const QByteArray &frameHeaders);
-
- void handleSYN_STREAM(char, quint32, const QByteArray &frameData);
- void handleSYN_REPLY(char flags, quint32, const QByteArray &frameData);
- void handleRST_STREAM(char flags, quint32 length, const QByteArray &frameData);
- void handleSETTINGS(char flags, quint32 length, const QByteArray &frameData);
- void handlePING(char, quint32 length, const QByteArray &frameData);
- void handleGOAWAY(char flags, quint32, const QByteArray &frameData);
- void handleHEADERS(char flags, quint32, const QByteArray &frameData);
- void handleWINDOW_UPDATE(char, quint32, const QByteArray &frameData);
-
- qint32 generateNextStreamID();
- void parseHttpHeaders(char flags, const QByteArray &frameData);
-
- void replyFinished(QHttpNetworkReply *httpReply, qint32 streamID);
- void replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID,
- QNetworkReply::NetworkError errorCode, const char *errorMessage);
-
- qint32 m_nextStreamID;
- QHash<quint32, HttpMessagePair> m_inFlightStreams;
- qint32 m_maxConcurrentStreams;
- quint32 m_initialWindowSize;
- QByteDataBuffer m_spdyBuffer;
- bool m_waitingForCompleteStream;
- z_stream m_deflateStream;
- z_stream m_inflateStream;
- QHash<QObject *, qint32> m_streamIDs;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::DataFrameFlags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::ControlFrameFlags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_Flags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_ID_Flags)
-
-QT_END_NAMESPACE
-
-#endif // !defined(QT_NO_SSL)
-
-#endif // QSPDYPROTOCOLHANDLER_H
diff --git a/src/network/android/jar/.gitignore b/src/network/android/jar/.gitignore
new file mode 100644
index 0000000000..364420a59a
--- /dev/null
+++ b/src/network/android/jar/.gitignore
@@ -0,0 +1,6 @@
+.gradle/
+build/
+gradle/
+gradlew
+gradlew.bat
+local.properties
diff --git a/src/network/android/jar/CMakeLists.txt b/src/network/android/jar/CMakeLists.txt
new file mode 100644
index 0000000000..b5172b6aba
--- /dev/null
+++ b/src/network/android/jar/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(java_sources
+ src/org/qtproject/qt/android/network/QtNetwork.java
+)
+
+qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork
+ INCLUDE_JARS ${QT_ANDROID_JAR}
+ SOURCES ${java_sources}
+ OUTPUT_DIR "${QT_BUILD_DIR}/jar"
+)
+
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
+install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork
+ DESTINATION ${destination}
+ COMPONENT Devel
+)
diff --git a/src/network/android/jar/build.gradle b/src/network/android/jar/build.gradle
new file mode 100644
index 0000000000..ea6d06c257
--- /dev/null
+++ b/src/network/android/jar/build.gradle
@@ -0,0 +1,51 @@
+// This is mainly used to allow Android Studio to easily read this folder as an android project.
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.4.0'
+ }
+}
+
+apply plugin: 'com.android.library'
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+}
+
+repositories {
+ google()
+ mavenCentral()
+}
+
+android {
+ compileSdk 34
+
+ defaultConfig {
+ minSdkVersion 28
+ }
+
+ sourceSets {
+ main {
+ java.srcDir 'src/'
+ resources.srcDir 'libs/'
+ manifest.srcFile 'AndroidManifest.xml'
+ res.srcDirs = ['res/']
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ android {
+ lintOptions {
+ abortOnError true
+ }
+ }
+}
diff --git a/src/network/android/jar/settings.gradle b/src/network/android/jar/settings.gradle
new file mode 100644
index 0000000000..a5d909ed33
--- /dev/null
+++ b/src/network/android/jar/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "QtAndroidNetwork"
diff --git a/src/network/android/jar/src/org/qtproject/qt/android/network/QtNetwork.java b/src/network/android/jar/src/org/qtproject/qt/android/network/QtNetwork.java
new file mode 100644
index 0000000000..eb6a16d5b7
--- /dev/null
+++ b/src/network/android/jar/src/org/qtproject/qt/android/network/QtNetwork.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android.network;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Proxy;
+import android.net.ProxyInfo;
+
+public class QtNetwork
+{
+ private static final String LOG_TAG = "QtNetwork";
+ private static ProxyReceiver m_proxyReceiver = null;
+ private static final Object m_lock = new Object();
+ private static ProxyInfo m_proxyInfo = null;
+
+ private static class ProxyReceiver extends BroadcastReceiver
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ m_proxyInfo = null;
+ }
+ }
+
+ private QtNetwork() {}
+
+ public static void registerReceiver(final Context context)
+ {
+ synchronized (m_lock) {
+ if (m_proxyReceiver == null) {
+ m_proxyReceiver = new ProxyReceiver();
+ IntentFilter intentFilter = new IntentFilter(Proxy.PROXY_CHANGE_ACTION);
+ context.registerReceiver(m_proxyReceiver, intentFilter);
+ }
+ }
+ }
+
+ public static void unregisterReceiver(final Context context)
+ {
+ synchronized (m_lock) {
+ if (m_proxyReceiver == null)
+ return;
+
+ context.unregisterReceiver(m_proxyReceiver);
+ }
+ }
+
+ public static ConnectivityManager getConnectivityManager(final Context context)
+ {
+ return (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ public static ProxyInfo getProxyInfo(final Context context)
+ {
+ if (m_proxyInfo == null)
+ m_proxyInfo = (ProxyInfo)getConnectivityManager(context).getDefaultProxy();
+ return m_proxyInfo;
+ }
+}
diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri
deleted file mode 100644
index d58d5ec168..0000000000
--- a/src/network/bearer/bearer.pri
+++ /dev/null
@@ -1,19 +0,0 @@
-# Qt network bearer management module
-
-HEADERS += bearer/qnetworkconfiguration.h \
- bearer/qnetworksession.h \
- bearer/qnetworkconfigmanager.h \
- bearer/qnetworkconfigmanager_p.h \
- bearer/qnetworkconfiguration_p.h \
- bearer/qnetworksession_p.h \
- bearer/qbearerengine_p.h \
- bearer/qbearerplugin_p.h \
- bearer/qsharednetworksession_p.h
-
-SOURCES += bearer/qnetworksession.cpp \
- bearer/qnetworkconfigmanager.cpp \
- bearer/qnetworkconfiguration.cpp \
- bearer/qnetworkconfigmanager_p.cpp \
- bearer/qbearerengine.cpp \
- bearer/qbearerplugin.cpp \
- bearer/qsharednetworksession.cpp
diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp
deleted file mode 100644
index 06bf449611..0000000000
--- a/src/network/bearer/qbearerengine.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qbearerengine_p.h"
-#include <QtCore/private/qlocking_p.h>
-
-#include <algorithm>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-static void cleanUpConfigurations(QHash<QString, QNetworkConfigurationPrivatePointer> &configurations)
-{
- for (auto &ptr : qExchange(configurations, {})) {
- ptr->isValid = false;
- ptr->id.clear();
- }
-}
-
-static bool hasUsedConfiguration(const QHash<QString, QNetworkConfigurationPrivatePointer> &configurations)
-{
- auto isUsed = [](const QNetworkConfigurationPrivatePointer &ptr) {
- return ptr->ref.loadRelaxed() > 1;
- };
- const auto end = configurations.end();
- return std::find_if(configurations.begin(), end, isUsed) != end;
-}
-
-QBearerEngine::QBearerEngine(QObject *parent)
- : QObject(parent)
-{
-}
-
-QBearerEngine::~QBearerEngine()
-{
- cleanUpConfigurations(snapConfigurations);
- cleanUpConfigurations(accessPointConfigurations);
- cleanUpConfigurations(userChoiceConfigurations);
-}
-
-bool QBearerEngine::requiresPolling() const
-{
- return false;
-}
-
-/*
- Returns \c true if configurations are in use; otherwise returns \c false.
-
- If configurations are in use and requiresPolling() returns \c true, polling will be enabled for
- this engine.
-*/
-bool QBearerEngine::configurationsInUse() const
-{
- const auto locker = qt_scoped_lock(mutex);
- return hasUsedConfiguration(accessPointConfigurations)
- || hasUsedConfiguration(snapConfigurations)
- || hasUsedConfiguration(userChoiceConfigurations);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qbearerengine_p.cpp"
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h
deleted file mode 100644
index c69f478b26..0000000000
--- a/src/network/bearer/qbearerengine_p.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBEARERENGINE_P_H
-#define QBEARERENGINE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworkconfiguration_p.h"
-#include "qnetworksession.h"
-#include "qnetworkconfigmanager.h"
-
-#include <QtCore/qobject.h>
-#include <QtCore/qglobal.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qhash.h>
-#include <QtCore/qsharedpointer.h>
-#include <QtCore/qmutex.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-class QNetworkConfiguration;
-
-class Q_NETWORK_EXPORT QBearerEngine : public QObject
-{
- Q_OBJECT
-
- friend class QNetworkConfigurationManagerPrivate;
-
-public:
- explicit QBearerEngine(QObject *parent = nullptr);
- virtual ~QBearerEngine();
-
- virtual bool hasIdentifier(const QString &id) = 0;
-
- virtual QNetworkConfigurationManager::Capabilities capabilities() const = 0;
-
- virtual QNetworkSessionPrivate *createSessionBackend() = 0;
-
- virtual QNetworkConfigurationPrivatePointer defaultConfiguration() = 0;
-
- virtual bool requiresPolling() const;
- bool configurationsInUse() const;
-
-Q_SIGNALS:
- void configurationAdded(QNetworkConfigurationPrivatePointer config);
- void configurationRemoved(QNetworkConfigurationPrivatePointer config);
- void configurationChanged(QNetworkConfigurationPrivatePointer config);
- void updateCompleted();
-
-protected:
- //this table contains an up to date list of all configs at any time.
- //it must be updated if configurations change, are added/removed or
- //the members of ServiceNetworks change
- QHash<QString, QNetworkConfigurationPrivatePointer> accessPointConfigurations;
- QHash<QString, QNetworkConfigurationPrivatePointer> snapConfigurations;
- QHash<QString, QNetworkConfigurationPrivatePointer> userChoiceConfigurations;
-
- mutable QRecursiveMutex mutex;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QBEARERENGINE_P_H
diff --git a/src/network/bearer/qbearerplugin.cpp b/src/network/bearer/qbearerplugin.cpp
deleted file mode 100644
index ec0d06e94c..0000000000
--- a/src/network/bearer/qbearerplugin.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qbearerplugin_p.h"
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-QBearerEnginePlugin::QBearerEnginePlugin(QObject *parent)
- : QObject(parent)
-{
-}
-
-QBearerEnginePlugin::~QBearerEnginePlugin()
-{
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h
deleted file mode 100644
index ac787d0541..0000000000
--- a/src/network/bearer/qbearerplugin_p.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QBEARERPLUGIN_P_H
-#define QBEARERPLUGIN_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qbearerengine_p.h"
-
-#include <QtCore/qplugin.h>
-#include <QtCore/qfactoryinterface.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-
-#define QBearerEngineFactoryInterface_iid "org.qt-project.Qt.QBearerEngineFactoryInterface"
-
-class Q_NETWORK_EXPORT QBearerEnginePlugin : public QObject
-{
- Q_OBJECT
-public:
- explicit QBearerEnginePlugin(QObject *parent = nullptr);
- virtual ~QBearerEnginePlugin();
-
- virtual QBearerEngine *create(const QString &key) const = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QBEARERPLUGIN_P_H
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
deleted file mode 100644
index 751735c8bd..0000000000
--- a/src/network/bearer/qnetworkconfigmanager.cpp
+++ /dev/null
@@ -1,386 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkconfigmanager.h"
-
-#include "qnetworkconfigmanager_p.h"
-#include "qbearerengine_p.h"
-
-#include <QtCore/qstringlist.h>
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qthread.h>
-#include <QtCore/private/qcoreapplication_p.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-static QBasicAtomicPointer<QNetworkConfigurationManagerPrivate> connManager_ptr;
-static QBasicAtomicInt appShutdown;
-
-static void connManager_prepare()
-{
- int shutdown = appShutdown.fetchAndStoreAcquire(0);
- Q_ASSERT(shutdown == 0 || shutdown == 1);
- Q_UNUSED(shutdown);
-}
-
-static void connManager_cleanup()
-{
- // this is not atomic or thread-safe!
- int shutdown = appShutdown.fetchAndStoreAcquire(1);
- Q_ASSERT(shutdown == 0);
- Q_UNUSED(shutdown);
- QNetworkConfigurationManagerPrivate *cmp = connManager_ptr.fetchAndStoreAcquire(nullptr);
- if (cmp)
- cmp->cleanup();
-}
-
-void QNetworkConfigurationManagerPrivate::addPreAndPostRoutine()
-{
- qAddPreRoutine(connManager_prepare);
- qAddPostRoutine(connManager_cleanup);
-}
-
-QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
-{
- QNetworkConfigurationManagerPrivate *ptr = connManager_ptr.loadAcquire();
- int shutdown = appShutdown.loadAcquire();
- if (!ptr && !shutdown) {
- static QBasicMutex connManager_mutex;
- QMutexLocker locker(&connManager_mutex);
- if (!(ptr = connManager_ptr.loadAcquire())) {
- ptr = new QNetworkConfigurationManagerPrivate;
-
- if (QCoreApplicationPrivate::mainThread() == QThread::currentThread()) {
- // right thread or no main thread yet
- ptr->addPreAndPostRoutine();
- ptr->initialize();
- } else {
- // wrong thread, we need to make the main thread do this
- QObject *obj = new QObject;
- QObject::connect(obj, SIGNAL(destroyed()), ptr, SLOT(addPreAndPostRoutine()), Qt::DirectConnection);
- ptr->initialize(); // this moves us to the right thread
- obj->moveToThread(QCoreApplicationPrivate::mainThread());
- obj->deleteLater();
- }
-
- connManager_ptr.storeRelease(ptr);
- }
- }
- return ptr;
-}
-
-/*!
- \class QNetworkConfigurationManager
-
- \brief The QNetworkConfigurationManager class manages the network configurations provided
- by the system.
-
- \since 4.7
-
- \inmodule QtNetwork
- \ingroup network
-
- QNetworkConfigurationManager provides access to the network configurations known to the system and
- enables applications to detect the system capabilities (with regards to network sessions) at runtime.
-
- A QNetworkConfiguration abstracts a set of configuration options describing how a
- network interface has to be configured to connect to a particular target network.
- QNetworkConfigurationManager maintains and updates the global list of
- QNetworkConfigurations. Applications can access and filter this list via
- allConfigurations(). If a new configuration is added or an existing one is removed or changed
- the configurationAdded(), configurationRemoved() and configurationChanged() signals are emitted
- respectively.
-
- The defaultConfiguration() can be used when intending to immediately create a new
- network session without caring about the particular configuration. It returns
- a \l QNetworkConfiguration::Discovered configuration. If there are not any
- discovered ones an invalid configuration is returned.
-
- Some configuration updates may require some time to perform updates. A WLAN scan is
- such an example. Unless the platform performs internal updates it may be required to
- manually trigger configuration updates via QNetworkConfigurationManager::updateConfigurations().
- The completion of the update process is indicated by emitting the updateCompleted()
- signal. The update process ensures that every existing QNetworkConfiguration instance
- is updated. There is no need to ask for a renewed configuration list via allConfigurations().
-
- \sa QNetworkConfiguration
-*/
-
-/*!
- \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration &config)
-
- This signal is emitted whenever a new network configuration is added to the system. The new
- configuration is specified by \a config.
-*/
-
-/*!
- \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration &config)
-
- This signal is emitted when a configuration is about to be removed from the system. The removed
- configuration, specified by \a config, is invalid but retains name and identifier.
-*/
-
-/*!
- \fn void QNetworkConfigurationManager::updateCompleted()
-
- This signal is emitted when the configuration update has been completed. Such an update can
- be initiated via \l updateConfigurations().
-*/
-
-/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration &config)
-
- This signal is emitted when the \l {QNetworkConfiguration::state()}{state} of \a config changes.
-*/
-
-/*!
- \fn void QNetworkConfigurationManager::onlineStateChanged(bool isOnline)
-
- This signal is emitted when the device changes from online to offline mode or vice versa.
- \a isOnline represents the new state of the device.
-
- The state is considered to be online for as long as
- \l{allConfigurations()}{allConfigurations}(QNetworkConfiguration::Active) returns a list with
- at least one entry.
-*/
-
-/*!
- \enum QNetworkConfigurationManager::Capability
-
- Specifies the system capabilities of the bearer API. The possible values are:
-
- \value CanStartAndStopInterfaces Network sessions and their underlying access points can be
- started and stopped. If this flag is not set QNetworkSession
- can only monitor but not influence the state of access points.
- On some platforms this feature may require elevated user
- permissions. This option is platform specific and may not
- always be available.
- \value DirectConnectionRouting Network sessions and their sockets can be bound to a
- particular network interface. Any packet that passes through
- the socket goes to the specified network interface and thus
- disregards standard routing table entries. This may be useful
- when two interfaces can reach overlapping IP ranges or an
- application has specific needs in regards to target networks.
- This option is platform specific and may not always be
- available.
- \value SystemSessionSupport If this flag is set the underlying platform ensures that a
- network interface is not shut down until the last network
- session has been \l{QNetworkSession::close()}{closed()}. This
- works across multiple processes. If the platform session
- support is missing this API can only ensure the above behavior
- for network sessions within the same process.
- In general mobile platforms have such
- support whereas most desktop platform lack this capability.
- \value ApplicationLevelRoaming The system gives applications control over the systems roaming
- behavior. Applications can initiate roaming (in case the
- current link is not suitable) and are consulted if the system
- has identified a more suitable access point.
- \value ForcedRoaming The system disconnects an existing access point and reconnects
- via a more suitable one. The application does not have any
- control over this process and has to reconnect its active
- sockets.
- \value DataStatistics If this flag is set QNetworkSession can provide statistics
- about transmitted and received data.
- \value NetworkSessionRequired If this flag is set the platform requires that a network
- session is created before network operations can be performed.
-*/
-
-/*!
- Constructs a QNetworkConfigurationManager with the given \a parent.
-
- Note that to ensure a valid list of current configurations immediately available, updating
- is done during construction which causes some delay.
-*/
-QNetworkConfigurationManager::QNetworkConfigurationManager(QObject *parent)
- : QObject(parent)
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv) {
- connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)),
- this, SIGNAL(configurationAdded(QNetworkConfiguration)));
- connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)),
- this, SIGNAL(configurationRemoved(QNetworkConfiguration)));
- connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)),
- this, SIGNAL(configurationChanged(QNetworkConfiguration)));
- connect(priv, SIGNAL(onlineStateChanged(bool)),
- this, SIGNAL(onlineStateChanged(bool)));
- connect(priv, SIGNAL(configurationUpdateComplete()),
- this, SIGNAL(updateCompleted()));
-
- priv->enablePolling();
- }
-}
-
-/*!
- Frees the resources associated with the QNetworkConfigurationManager object.
-*/
-QNetworkConfigurationManager::~QNetworkConfigurationManager()
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- priv->disablePolling();
-}
-
-
-/*!
- Returns the default configuration to be used. This function always returns a discovered
- configuration; otherwise an invalid configuration.
-
- In some cases it may be required to call updateConfigurations() and wait for the
- updateCompleted() signal before calling this function.
-
- \sa allConfigurations()
-*/
-QNetworkConfiguration QNetworkConfigurationManager::defaultConfiguration() const
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- return priv->defaultConfiguration();
-
- return QNetworkConfiguration();
-}
-
-/*!
- Returns the list of configurations which comply with the given \a filter.
-
- By default this function returns all (defined and undefined) configurations.
-
- A wireless network with a particular SSID may only be accessible in a
- certain area despite the fact that the system has a valid configuration
- for it. Therefore the filter flag may be used to limit the list to
- discovered and possibly connected configurations only.
-
- If \a filter is set to zero this function returns all possible configurations.
-
- Note that this function returns the states for all configurations as they are known at
- the time of this function call. If for instance a configuration of type WLAN is defined
- the system may have to perform a WLAN scan in order to determine whether it is
- actually available. To obtain the most accurate state updateConfigurations() should
- be used to update each configuration's state. Note that such an update may require
- some time. It's completion is signalled by updateCompleted(). In the absence of a
- configuration update this function returns the best estimate at the time of the call.
- Therefore, if WLAN configurations are of interest, it is recommended that
- updateConfigurations() is called once after QNetworkConfigurationManager
- instantiation (WLAN scans are too time consuming to perform in constructor).
- After this the data is kept automatically up-to-date as the system reports
- any changes.
-*/
-QList<QNetworkConfiguration> QNetworkConfigurationManager::allConfigurations(QNetworkConfiguration::StateFlags filter) const
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- return priv->allConfigurations(filter);
-
- return QList<QNetworkConfiguration>();
-}
-
-/*!
- Returns the QNetworkConfiguration for \a identifier; otherwise returns an
- invalid QNetworkConfiguration.
-
- \sa QNetworkConfiguration::identifier()
-*/
-QNetworkConfiguration QNetworkConfigurationManager::configurationFromIdentifier(const QString &identifier) const
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- return priv->configurationFromIdentifier(identifier);
-
- return QNetworkConfiguration();
-}
-
-/*!
- Returns \c true if the system is considered to be connected to another device via an active
- network interface; otherwise returns \c false.
-
- This is equivalent to the following code snippet:
-
- \snippet code/src_network_bearer_qnetworkconfigmanager.cpp 0
-
- \sa onlineStateChanged()
-*/
-bool QNetworkConfigurationManager::isOnline() const
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- return priv->isOnline();
-
- return false;
-}
-
-/*!
- Returns the capabilities supported by the current platform.
-*/
-QNetworkConfigurationManager::Capabilities QNetworkConfigurationManager::capabilities() const
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- return priv->capabilities();
-
- return {};
-}
-
-/*!
- Initiates an update of all configurations. This may be used to initiate WLAN scans or other
- time consuming updates which may be required to obtain the correct state for configurations.
-
- This call is asynchronous. On completion of this update the updateCompleted() signal is
- emitted. If new configurations are discovered or old ones were removed or changed the update
- process may trigger the emission of one or multiple configurationAdded(),
- configurationRemoved() and configurationChanged() signals.
-
- If a configuration state changes as a result of this update all existing QNetworkConfiguration
- instances are updated automatically.
-
- \sa allConfigurations()
-*/
-void QNetworkConfigurationManager::updateConfigurations()
-{
- QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
- if (priv)
- priv->performAsyncConfigurationUpdate();
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qnetworkconfigmanager.cpp"
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h
deleted file mode 100644
index e8866999c7..0000000000
--- a/src/network/bearer/qnetworkconfigmanager.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKCONFIGMANAGER_H
-#define QNETWORKCONFIGMANAGER_H
-
-#include <QtNetwork/qtnetworkglobal.h>
-#include <QtCore/qobject.h>
-#include <QtNetwork/qnetworkconfiguration.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-class QNetworkConfigurationManagerPrivate;
-class Q_NETWORK_EXPORT QNetworkConfigurationManager : public QObject
-{
- Q_OBJECT
-
-public:
- enum Capability {
- CanStartAndStopInterfaces = 0x00000001,
- DirectConnectionRouting = 0x00000002,
- SystemSessionSupport = 0x00000004,
- ApplicationLevelRoaming = 0x00000008,
- ForcedRoaming = 0x00000010,
- DataStatistics = 0x00000020,
- NetworkSessionRequired = 0x00000040
- };
-
- Q_DECLARE_FLAGS(Capabilities, Capability)
-
- explicit QNetworkConfigurationManager(QObject *parent = nullptr);
- virtual ~QNetworkConfigurationManager();
-
- QNetworkConfigurationManager::Capabilities capabilities() const;
-
- QNetworkConfiguration defaultConfiguration() const;
- QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags flags = QNetworkConfiguration::StateFlags()) const;
- QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
-
- bool isOnline() const;
-
-public Q_SLOTS:
- void updateConfigurations();
-
-Q_SIGNALS:
- void configurationAdded(const QNetworkConfiguration &config);
- void configurationRemoved(const QNetworkConfiguration &config);
- void configurationChanged(const QNetworkConfiguration &config);
- void onlineStateChanged(bool isOnline);
- void updateCompleted();
-
-private:
- Q_DISABLE_COPY(QNetworkConfigurationManager)
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkConfigurationManager::Capabilities)
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QNETWORKCONFIGMANAGER_H
diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp
deleted file mode 100644
index f0aa452dd3..0000000000
--- a/src/network/bearer/qnetworkconfigmanager_p.cpp
+++ /dev/null
@@ -1,517 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkconfigmanager_p.h"
-#include "qbearerplugin_p.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qtimer.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qthread.h>
-#include <QtCore/private/qcoreapplication_p.h>
-#include <QtCore/private/qlocking_p.h>
-#include <QtCore/private/qthread_p.h>
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qglobal.h>
-
-#include <utility>
-
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
- : QObject(), pollTimer(nullptr),
- loader(QBearerEngineFactoryInterface_iid, QLatin1String("/bearer")),
- forcedPolling(0), firstUpdate(true)
-{
- qRegisterMetaType<QNetworkConfiguration>();
- qRegisterMetaType<QNetworkConfigurationPrivatePointer>();
-}
-
-void QNetworkConfigurationManagerPrivate::initialize()
-{
- //Two stage construction, because we only want to do this heavyweight work for the winner of the Q_GLOBAL_STATIC race.
- bearerThread = new QDaemonThread();
- bearerThread->setObjectName(QStringLiteral("Qt bearer thread"));
-
- bearerThread->moveToThread(QCoreApplicationPrivate::mainThread()); // because cleanup() is called in main thread context.
- moveToThread(bearerThread);
- bearerThread->start();
- updateConfigurations();
-}
-
-QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate()
-{
- QMutexLocker locker(&mutex);
-
- qDeleteAll(sessionEngines);
- sessionEngines.clear();
- if (bearerThread)
- bearerThread->quit();
-}
-
-void QNetworkConfigurationManagerPrivate::cleanup()
-{
- QThread* thread = bearerThread;
- deleteLater();
- if (thread->wait(QDeadlineTimer(5000)))
- delete thread;
-}
-
-QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const
-{
- QMutexLocker locker(&mutex);
-
- for (QBearerEngine *engine : sessionEngines) {
- QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration();
- if (ptr) {
- QNetworkConfiguration config;
- config.d = ptr;
- return config;
- }
- }
-
- // Engines don't have a default configuration.
-
- // Return first active snap
- QNetworkConfigurationPrivatePointer defaultConfiguration;
-
- for (QBearerEngine *engine : sessionEngines) {
- const auto locker = qt_scoped_lock(engine->mutex);
-
- for (const auto &ptr : qAsConst(engine->snapConfigurations)) {
- const auto locker = qt_scoped_lock(ptr->mutex);
-
- if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
- QNetworkConfiguration config;
- config.d = ptr;
- return config;
- } else if (!defaultConfiguration) {
- if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered)
- defaultConfiguration = ptr;
- }
- }
- }
-
- // No Active SNAPs return first Discovered SNAP.
- if (defaultConfiguration) {
- QNetworkConfiguration config;
- config.d = defaultConfiguration;
- return config;
- }
-
- /*
- No Active or Discovered SNAPs, find the perferred access point.
- The following priority order is used:
-
- 1. Active Ethernet
- 2. Active WLAN
- 3. Active Other
- 4. Discovered Ethernet
- 5. Discovered WLAN
- 6. Discovered Other
- */
-
- for (QBearerEngine *engine : sessionEngines) {
-
- QMutexLocker locker(&engine->mutex);
-
- for (const auto &ptr : qAsConst(engine->accessPointConfigurations)) {
-
- QMutexLocker configLocker(&ptr->mutex);
- QNetworkConfiguration::BearerType bearerType = ptr->bearerType;
-
- if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) {
- if (!defaultConfiguration) {
- defaultConfiguration = ptr;
- } else {
- QMutexLocker defaultConfigLocker(&defaultConfiguration->mutex);
-
- if (defaultConfiguration->state == ptr->state) {
- switch (defaultConfiguration->bearerType) {
- case QNetworkConfiguration::BearerEthernet:
- // do nothing
- break;
- case QNetworkConfiguration::BearerWLAN:
- // Ethernet beats WLAN
- defaultConfiguration = ptr;
- break;
- default:
- // Ethernet and WLAN beats other
- if (bearerType == QNetworkConfiguration::BearerEthernet ||
- bearerType == QNetworkConfiguration::BearerWLAN) {
- defaultConfiguration = ptr;
- }
- }
- } else {
- // active beats discovered
- if ((defaultConfiguration->state & QNetworkConfiguration::Active) !=
- QNetworkConfiguration::Active) {
- defaultConfiguration = ptr;
- }
- }
- }
- }
- }
- }
-
- // No Active InternetAccessPoint return first Discovered InternetAccessPoint.
- if (defaultConfiguration) {
- QNetworkConfiguration config;
- config.d = defaultConfiguration;
- return config;
- }
-
- return QNetworkConfiguration();
-}
-
-QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) const
-{
- QList<QNetworkConfiguration> result;
-
- QMutexLocker locker(&mutex);
-
- for (QBearerEngine *engine : sessionEngines) {
-
- const auto locker = qt_scoped_lock(engine->mutex);
-
- //find all InternetAccessPoints
- for (const auto &ptr : qAsConst(engine->accessPointConfigurations)) {
- const auto locker = qt_scoped_lock(ptr->mutex);
-
- if ((ptr->state & filter) == filter) {
- QNetworkConfiguration pt;
- pt.d = ptr;
- result << pt;
- }
- }
-
- //find all service networks
- for (const auto &ptr : qAsConst(engine->snapConfigurations)) {
- const auto locker = qt_scoped_lock(ptr->mutex);
-
- if ((ptr->state & filter) == filter) {
- QNetworkConfiguration pt;
- pt.d = ptr;
- result << pt;
- }
- }
- }
-
- return result;
-}
-
-QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) const
-{
- QNetworkConfiguration item;
-
- const auto locker = qt_scoped_lock(mutex);
-
- for (QBearerEngine *engine : sessionEngines) {
- const auto locker = qt_scoped_lock(engine->mutex);
- if (auto ptr = engine->accessPointConfigurations.value(identifier)) {
- item.d = std::move(ptr);
- break;
- }
- if (auto ptr = engine->snapConfigurations.value(identifier)) {
- item.d = std::move(ptr);
- break;
- }
- if (auto ptr = engine->userChoiceConfigurations.value(identifier)) {
- item.d = std::move(ptr);
- break;
- }
- }
-
- return item;
-}
-
-bool QNetworkConfigurationManagerPrivate::isOnline() const
-{
- const auto locker = qt_scoped_lock(mutex);
-
- // We need allConfigurations since onlineConfigurations is filled with queued connections
- // and thus is not always (more importantly just after creation) up to date
- return !allConfigurations(QNetworkConfiguration::Active).isEmpty();
-}
-
-QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const
-{
- const auto locker = qt_scoped_lock(mutex);
-
- QNetworkConfigurationManager::Capabilities capFlags;
-
- for (QBearerEngine *engine : sessionEngines)
- capFlags |= engine->capabilities();
-
- return capFlags;
-}
-
-void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurationPrivatePointer ptr)
-{
- const auto locker = qt_scoped_lock(mutex);
-
- if (!firstUpdate) {
- QNetworkConfiguration item;
- item.d = ptr;
- emit configurationAdded(item);
- }
-
- auto ptrLocker = qt_unique_lock(ptr->mutex);
- if (ptr->state == QNetworkConfiguration::Active) {
- const auto id = ptr->id;
- ptrLocker.unlock();
- onlineConfigurations.insert(id);
- if (!firstUpdate && onlineConfigurations.count() == 1)
- emit onlineStateChanged(true);
- }
-}
-
-void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigurationPrivatePointer ptr)
-{
- const auto locker = qt_scoped_lock(mutex);
-
- {
- const auto locker = qt_scoped_lock(ptr->mutex);
- ptr->isValid = false;
- }
-
- if (!firstUpdate) {
- QNetworkConfiguration item;
- item.d = ptr;
- emit configurationRemoved(item);
- }
-
- onlineConfigurations.remove(ptr->id);
- if (!firstUpdate && onlineConfigurations.isEmpty())
- emit onlineStateChanged(false);
-}
-
-void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigurationPrivatePointer ptr)
-{
- const auto locker = qt_scoped_lock(mutex);
-
- if (!firstUpdate) {
- QNetworkConfiguration item;
- item.d = ptr;
- emit configurationChanged(item);
- }
-
- bool previous = !onlineConfigurations.isEmpty();
-
- {
- const auto locker = qt_scoped_lock(ptr->mutex);
- if (ptr->state == QNetworkConfiguration::Active)
- onlineConfigurations.insert(ptr->id);
- else
- onlineConfigurations.remove(ptr->id);
- }
-
- bool online = !onlineConfigurations.isEmpty();
-
- if (!firstUpdate && online != previous)
- emit onlineStateChanged(online);
-}
-
-void QNetworkConfigurationManagerPrivate::updateConfigurations()
-{
- typedef QMultiMap<int, QString> PluginKeyMap;
- typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
-
- auto locker = qt_unique_lock(mutex);
-
- if (firstUpdate) {
- if (qobject_cast<QBearerEngine *>(sender()))
- return;
-
- updating = false;
-
- bool envOK = false;
- const int skipGeneric = qEnvironmentVariableIntValue("QT_EXCLUDE_GENERIC_BEARER", &envOK);
- QBearerEngine *generic = nullptr;
- QFactoryLoader *l = &loader;
- const PluginKeyMap keyMap = l->keyMap();
- const PluginKeyMapConstIterator cend = keyMap.constEnd();
- QStringList addedEngines;
- for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) {
- const QString &key = it.value();
- if (addedEngines.contains(key))
- continue;
-
- addedEngines.append(key);
- if (QBearerEngine *engine = qLoadPlugin<QBearerEngine, QBearerEnginePlugin>(l, key)) {
- if (key == QLatin1String("generic"))
- generic = engine;
- else
- sessionEngines.append(engine);
-
- engine->moveToThread(bearerThread);
-
- connect(engine, SIGNAL(updateCompleted()),
- this, SLOT(updateConfigurations()),
- Qt::QueuedConnection);
- connect(engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)),
- this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer)),
- Qt::QueuedConnection);
- connect(engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)),
- this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer)),
- Qt::QueuedConnection);
- connect(engine, SIGNAL(configurationChanged(QNetworkConfigurationPrivatePointer)),
- this, SLOT(configurationChanged(QNetworkConfigurationPrivatePointer)),
- Qt::QueuedConnection);
- }
- }
-
- if (generic) {
- if (!envOK || skipGeneric <= 0)
- sessionEngines.append(generic);
- else
- delete generic;
- }
- }
-
- QBearerEngine *engine = qobject_cast<QBearerEngine *>(sender());
- if (engine && !updatingEngines.isEmpty())
- updatingEngines.remove(engine);
-
- if (updating && updatingEngines.isEmpty()) {
- updating = false;
- emit configurationUpdateComplete();
- }
-
- if (engine && !pollingEngines.isEmpty()) {
- pollingEngines.remove(engine);
- if (pollingEngines.isEmpty())
- startPolling();
- }
-
- if (firstUpdate) {
- firstUpdate = false;
- const QList<QBearerEngine*> enginesToInitialize = sessionEngines; //shallow copy the list in case it is modified when we unlock mutex
- locker.unlock();
- for (QBearerEngine* engine : enginesToInitialize)
- QMetaObject::invokeMethod(engine, "initialize", Qt::BlockingQueuedConnection);
- }
-}
-
-void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate()
-{
- const auto locker = qt_scoped_lock(mutex);
-
- if (sessionEngines.isEmpty()) {
- emit configurationUpdateComplete();
- return;
- }
-
- updating = true;
-
- for (QBearerEngine *engine : qAsConst(sessionEngines)) {
- updatingEngines.insert(engine);
- QMetaObject::invokeMethod(engine, "requestUpdate");
- }
-}
-
-QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const
-{
- const auto locker = qt_scoped_lock(mutex);
-
- return sessionEngines;
-}
-
-void QNetworkConfigurationManagerPrivate::startPolling()
-{
- const auto locker = qt_scoped_lock(mutex);
- if (!pollTimer) {
- pollTimer = new QTimer(this);
- bool ok;
- int interval = qEnvironmentVariableIntValue("QT_BEARER_POLL_TIMEOUT", &ok);
- if (!ok)
- interval = 10000;//default 10 seconds
- pollTimer->setInterval(interval);
- pollTimer->setSingleShot(true);
- connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines()));
- }
-
- if (pollTimer->isActive())
- return;
-
- for (QBearerEngine *engine : qAsConst(sessionEngines)) {
- if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
- pollTimer->start();
- break;
- }
- }
- performAsyncConfigurationUpdate();
-}
-
-void QNetworkConfigurationManagerPrivate::pollEngines()
-{
- const auto locker = qt_scoped_lock(mutex);
-
- for (QBearerEngine *engine : qAsConst(sessionEngines)) {
- if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
- pollingEngines.insert(engine);
- QMetaObject::invokeMethod(engine, "requestUpdate");
- }
- }
-}
-
-void QNetworkConfigurationManagerPrivate::enablePolling()
-{
- const auto locker = qt_scoped_lock(mutex);
-
- ++forcedPolling;
-
- if (forcedPolling == 1)
- QMetaObject::invokeMethod(this, "startPolling");
-}
-
-void QNetworkConfigurationManagerPrivate::disablePolling()
-{
- const auto locker = qt_scoped_lock(mutex);
-
- --forcedPolling;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h
deleted file mode 100644
index 4819c2027c..0000000000
--- a/src/network/bearer/qnetworkconfigmanager_p.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKCONFIGMANAGER_P_H
-#define QNETWORKCONFIGMANAGER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworkconfigmanager.h"
-#include "qnetworkconfiguration_p.h"
-
-#include <QtCore/private/qfactoryloader_p.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qset.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-class QBearerEngine;
-class QTimer;
-
-class Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate : public QObject
-{
- Q_OBJECT
-
-public:
- QNetworkConfigurationManagerPrivate();
- virtual ~QNetworkConfigurationManagerPrivate();
-
- QNetworkConfiguration defaultConfiguration() const;
- QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter) const;
- QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
-
- bool isOnline() const;
-
- QNetworkConfigurationManager::Capabilities capabilities() const;
-
- void performAsyncConfigurationUpdate();
-
- QList<QBearerEngine *> engines() const;
-
- void enablePolling();
- void disablePolling();
-
- void initialize();
- void cleanup();
-public Q_SLOTS:
- void updateConfigurations();
-
- static void addPreAndPostRoutine();
-
-Q_SIGNALS:
- void configurationAdded(const QNetworkConfiguration &config);
- void configurationRemoved(const QNetworkConfiguration &config);
- void configurationChanged(const QNetworkConfiguration &config);
- void configurationUpdateComplete();
- void onlineStateChanged(bool isOnline);
-
-private Q_SLOTS:
- void configurationAdded(QNetworkConfigurationPrivatePointer ptr);
- void configurationRemoved(QNetworkConfigurationPrivatePointer ptr);
- void configurationChanged(QNetworkConfigurationPrivatePointer ptr);
-
- void pollEngines();
-
-
-private:
- Q_INVOKABLE void startPolling();
- QTimer *pollTimer;
- QThread *bearerThread;
-
-private:
- mutable QRecursiveMutex mutex;
-
- QFactoryLoader loader;
- QList<QBearerEngine *> sessionEngines;
-
- QSet<QString> onlineConfigurations;
-
- QSet<QBearerEngine *> pollingEngines;
- QSet<QBearerEngine *> updatingEngines;
- int forcedPolling;
- bool updating;
-
- bool firstUpdate;
-};
-
-Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate();
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QNETWORKCONFMANAGER_P_H
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
deleted file mode 100644
index 25c4ab711e..0000000000
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ /dev/null
@@ -1,593 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkconfiguration.h"
-#include "qnetworkconfiguration_p.h"
-#include <QDebug>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QNetworkConfiguration
-
- \brief The QNetworkConfiguration class provides an abstraction of one or more access point configurations.
-
- \since 4.7
-
- \inmodule QtNetwork
- \ingroup network
- \ingroup shared
-
- QNetworkConfiguration encapsulates a single access point or service network.
- In most cases a single access point configuration can be mapped to one network
- interface. However a single network interface may not always map to only one
- access point configuration. Multiple configurations for the same
- network device may enable multiple access points. An example
- device that could exhibit such a configuration might be a
- Smartphone which allows the user to manage multiple WLAN
- configurations while the device itself has only one WLAN network device.
-
- The QNetworkConfiguration also supports the concept of service networks.
- This concept allows the grouping of multiple access point configurations
- into one entity. Such a group is called service network and can be
- beneficial in cases whereby a network session to a
- particular destination network is required (e.g. a company network).
- When using a service network the user doesn't usually care which one of the
- connectivity options is chosen (e.g. corporate WLAN or VPN via GPRS)
- as long as he can reach the company's target server. Depending
- on the current position and time some of the access points that make
- up the service network may not even be available. Furthermore
- automated access point roaming can be enabled which enables the device
- to change the network interface configuration dynamically while maintaining
- the applications connection to the target network. It allows adaption
- to the changing environment and may enable optimization with regards to
- cost, speed or other network parameters.
-
- Special configurations of type UserChoice provide a placeholder configuration which is
- resolved to an actual network configuration by the platform when a
- \l {QNetworkSession}{session} is \l {QNetworkSession::open()}{opened}. Not all platforms
- support the concept of a user choice configuration.
-
- \section1 Configuration States
-
- The list of available configurations can be obtained via
- QNetworkConfigurationManager::allConfigurations(). A configuration can have
- multiple states. The \l Defined configuration state indicates that the configuration
- is stored on the device. However the configuration is not yet ready to be activated
- as e.g. a WLAN may not be available at the current time.
-
- The \l Discovered state implies that the configuration is \l Defined and
- the outside conditions are such that the configuration can be used immediately
- to open a new network session. An example of such an outside condition may be
- that the Ethernet cable is actually connected to the device or that the WLAN
- with the specified SSID is in range.
-
- The \l Active state implies that the configuration is \l Discovered. A configuration
- in this state is currently being used by an application. The underlying network
- interface has a valid IP configuration and can transfer IP packets between the
- device and the target network.
-
- The \l Undefined state indicates that the system has knowledge of possible target
- networks but cannot actually use that knowledge to connect to it. An example
- for such a state could be an encrypted WLAN that has been discovered
- but the user hasn't actually saved a configuration including the required password
- which would allow the device to connect to it.
-
- Depending on the type of configuration some states are transient in nature. A GPRS/UMTS
- connection may almost always be \l Discovered if the GSM/UMTS network is available.
- However if the GSM/UMTS network loses the connection the associated configuration may change its state
- from \l Discovered to \l Defined as well. A similar use case might be triggered by
- WLAN availability. QNetworkConfigurationManager::updateConfigurations() can be used to
- manually trigger updates of states. Note that some platforms do not require such updates
- as they implicitly change the state once it has been discovered. If the state of a
- configuration changes all related QNetworkConfiguration instances change their state automatically.
-
- \sa QNetworkSession, QNetworkConfigurationManager
-*/
-
-/*!
- \enum QNetworkConfiguration::Type
-
- This enum describes the type of configuration.
-
- \value InternetAccessPoint The configuration specifies the details for a single access point.
- Note that configurations of type InternetAccessPoint may be part
- of other QNetworkConfigurations of type ServiceNetwork.
- \value ServiceNetwork The configuration is based on a group of QNetworkConfigurations of
- type InternetAccessPoint. All group members can reach the same
- target network. This type of configuration is a mandatory
- requirement for roaming enabled network sessions. On some
- platforms this form of configuration may also be called Service
- Network Access Point (SNAP).
- \value UserChoice The configuration is a placeholder which will be resolved to an
- actual configuration by the platform when a session is opened. Depending
- on the platform the selection may generate a popup dialog asking the user
- for his preferred choice.
- \value Invalid The configuration is invalid.
-*/
-
-/*!
- \enum QNetworkConfiguration::StateFlag
-
- Specifies the configuration states.
-
- \value Undefined This state is used for transient configurations such as newly discovered
- WLANs for which the user has not actually created a configuration yet.
- \value Defined Defined configurations are known to the system but are not immediately
- usable (e.g. a configured WLAN is not within range or the Ethernet cable
- is currently not plugged into the machine).
- \value Discovered A discovered configuration can be immediately used to create a new
- QNetworkSession. An example of a discovered configuration could be a WLAN
- which is within in range. If the device moves out of range the discovered
- flag is dropped. A second example is a GPRS configuration which generally
- remains discovered for as long as the device has network coverage. A
- configuration that has this state is also in state
- QNetworkConfiguration::Defined. If the configuration is a service network
- this flag is set if at least one of the underlying access points
- configurations has the Discovered state.
- \value Active The configuration is currently used by an open network session
- (see \l QNetworkSession::isOpen()). However this does not mean that the
- current process is the entity that created the open session. It merely
- indicates that if a new QNetworkSession were to be constructed based on
- this configuration \l QNetworkSession::state() would return
- \l QNetworkSession::Connected. This state implies the
- QNetworkConfiguration::Discovered state.
-*/
-
-/*!
- \enum QNetworkConfiguration::Purpose
-
- Specifies the purpose of the configuration.
-
- \value UnknownPurpose The configuration doesn't specify any purpose. This is the default value.
- \value PublicPurpose The configuration can be used for general purpose internet access.
- \value PrivatePurpose The configuration is suitable to access a private network such as an office Intranet.
- \value ServiceSpecificPurpose The configuration can be used for operator specific services (e.g.
- receiving MMS messages or content streaming).
-*/
-
-/*!
- \enum QNetworkConfiguration::BearerType
-
- Specifies the type of bearer used by a configuration.
-
- \value BearerUnknown The type of bearer is unknown or unspecified. The bearerTypeName()
- function may return additional information.
- \value BearerEthernet The configuration is for an Ethernet interfaces.
- \value BearerWLAN The configuration is for a Wireless LAN interface.
- \value Bearer2G The configuration is for a CSD, GPRS, HSCSD, EDGE or cdmaOne interface.
- \value Bearer3G The configuration is for a 3G interface.
- \value Bearer4G The configuration is for a 4G interface.
- \value BearerCDMA2000 The configuration is for CDMA interface.
- \value BearerWCDMA The configuration is for W-CDMA/UMTS interface.
- \value BearerHSPA The configuration is for High Speed Packet Access (HSPA) interface.
- \value BearerBluetooth The configuration is for a Bluetooth interface.
- \value BearerWiMAX The configuration is for a WiMAX interface.
- \value BearerEVDO The configuration is for an EVDO (3G) interface.
- \value BearerLTE The configuration is for a LTE (4G) interface.
-*/
-
-/*!
- Constructs an invalid configuration object.
-
- \sa isValid()
-*/
-QNetworkConfiguration::QNetworkConfiguration()
- : d(nullptr)
-{
-}
-
-/*!
- Creates a copy of the QNetworkConfiguration object contained in \a other.
-*/
-QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration &other)
- : d(other.d)
-{
-}
-
-/*!
- Frees the resources associated with the QNetworkConfiguration object.
-*/
-QNetworkConfiguration::~QNetworkConfiguration()
-{
-}
-
-/*!
- Copies the content of the QNetworkConfiguration object contained in \a other into this one.
-*/
-QNetworkConfiguration &QNetworkConfiguration::operator=(const QNetworkConfiguration &other)
-{
- d = other.d;
- return *this;
-}
-
-/*!
- \fn void QNetworkConfiguration::swap(QNetworkConfiguration &other)
- \since 5.0
-
- Swaps this network configuration with \a other. This function is
- very fast and never fails.
-*/
-
-/*!
- Returns \c true, if this configuration is the same as the \a other
- configuration given; otherwise returns \c false.
-*/
-bool QNetworkConfiguration::operator==(const QNetworkConfiguration &other) const
-{
- return (d == other.d);
-}
-
-/*!
- \fn bool QNetworkConfiguration::operator!=(const QNetworkConfiguration &other) const
-
- Returns \c true if this configuration is not the same as the \a other
- configuration given; otherwise returns \c false.
-*/
-
-/*!
- Returns the user visible name of this configuration.
-
- The name may either be the name of the underlying access point or the
- name for service network that this configuration represents.
-*/
-QString QNetworkConfiguration::name() const
-{
- if (!d)
- return QString();
-
- QMutexLocker locker(&d->mutex);
- return d->name;
-}
-
-/*!
- Returns the unique and platform specific identifier for this network configuration;
- otherwise an empty string.
-*/
-QString QNetworkConfiguration::identifier() const
-{
- if (!d)
- return QString();
-
- QMutexLocker locker(&d->mutex);
- return d->id;
-}
-
-/*!
- Returns the type of the configuration.
-
- A configuration can represent a single access point configuration or
- a set of access point configurations. Such a set is called service network.
- A configuration that is based on a service network can potentially support
- roaming of network sessions.
-*/
-QNetworkConfiguration::Type QNetworkConfiguration::type() const
-{
- if (!d)
- return QNetworkConfiguration::Invalid;
-
- QMutexLocker locker(&d->mutex);
- return d->type;
-}
-
-/*!
- Returns \c true if this QNetworkConfiguration object is valid.
- A configuration may become invalid if the user deletes the configuration or
- the configuration was default-constructed.
-
- The addition and removal of configurations can be monitored via the
- QNetworkConfigurationManager.
-
- \sa QNetworkConfigurationManager
-*/
-bool QNetworkConfiguration::isValid() const
-{
- if (!d)
- return false;
-
- QMutexLocker locker(&d->mutex);
- return d->isValid;
-}
-
-/*!
- \since 5.9
-
- Returns the connect timeout of this configuration.
-
- \sa setConnectTimeout
-*/
-int QNetworkConfiguration::connectTimeout() const
-{
- if (!d)
- return QNetworkConfigurationPrivate::DefaultTimeout;
- QMutexLocker locker(&d->mutex);
- return d->timeout;
-}
-
-/*!
- \since 5.9
-
- Sets the connect timeout of this configuration to \a timeout.
- This allows control of the timeout used by \c QAbstractSocket
- to establish a connection.
-
- \note \a timeout is in millisecond.
-
- \warning This will have no effect if the bearer plugin doesn't have
- the CanStartAndStopInterfaces capability.
-
- Returns true if succeeded.
-
- \sa connectTimeout
-*/
-bool QNetworkConfiguration::setConnectTimeout(int timeout)
-{
- if (!d)
- return false;
- QMutexLocker locker(&d->mutex);
- d->timeout = timeout;
- return true;
-}
-
-/*!
- Returns the current state of the configuration.
-*/
-QNetworkConfiguration::StateFlags QNetworkConfiguration::state() const
-{
- if (!d)
- return QNetworkConfiguration::Undefined;
-
- QMutexLocker locker(&d->mutex);
- return d->state;
-}
-
-/*!
- Returns the purpose of this configuration.
-
- The purpose field may be used to programmatically determine the
- purpose of a configuration. Such information is usually part of the
- access point or service network meta data.
-*/
-QNetworkConfiguration::Purpose QNetworkConfiguration::purpose() const
-{
- if (!d)
- return QNetworkConfiguration::UnknownPurpose;
-
- QMutexLocker locker(&d->mutex);
- return d->purpose;
-}
-
-/*!
- Returns \c true if this configuration supports roaming; otherwise false.
-*/
-bool QNetworkConfiguration::isRoamingAvailable() const
-{
- if (!d)
- return false;
-
- QMutexLocker locker(&d->mutex);
- return d->roamingSupported;
-}
-
-/*!
- Returns all sub configurations of this network configuration in priority order. The first sub
- configuration in the list has the highest priority.
-
- Only network configurations of type \l ServiceNetwork can have children. Otherwise this
- function returns an empty list.
-*/
-QList<QNetworkConfiguration> QNetworkConfiguration::children() const
-{
- return {};
-}
-
-/*!
- Returns the type of bearer used by this network configuration.
-
- If the bearer type is \l {QNetworkConfiguration::BearerUnknown}{unknown} the bearerTypeName()
- function can be used to retrieve a textural type name for the bearer.
-
- An invalid network configuration always returns the BearerUnknown value.
-
- \sa bearerTypeName(), bearerTypeFamily()
-*/
-QNetworkConfiguration::BearerType QNetworkConfiguration::bearerType() const
-{
- if (!isValid())
- return BearerUnknown;
-
- QMutexLocker locker(&d->mutex);
- return d->bearerType;
-}
-
-/*!
- \since 5.2
-
- Returns the bearer type family used by this network configuration.
- The following table lists how bearerType() values map to
- bearerTypeFamily() values:
-
- \table
- \header
- \li bearer type
- \li bearer type family
- \row
- \li BearerUnknown, Bearer2G, BearerEthernet, BearerWLAN,
- BearerBluetooth
- \li (same type)
- \row
- \li BearerCDMA2000, BearerEVDO, BearerWCDMA, BearerHSPA, Bearer3G
- \li Bearer3G
- \row
- \li BearerWiMAX, BearerLTE, Bearer4G
- \li Bearer4G
- \endtable
-
- An invalid network configuration always returns the BearerUnknown value.
-
- \sa bearerType(), bearerTypeName()
-*/
-QNetworkConfiguration::BearerType QNetworkConfiguration::bearerTypeFamily() const
-{
- QNetworkConfiguration::BearerType type = bearerType();
- switch (type) {
- case QNetworkConfiguration::BearerUnknown: // fallthrough
- case QNetworkConfiguration::Bearer2G: // fallthrough
- case QNetworkConfiguration::BearerEthernet: // fallthrough
- case QNetworkConfiguration::BearerWLAN: // fallthrough
- case QNetworkConfiguration::BearerBluetooth:
- return type;
- case QNetworkConfiguration::BearerCDMA2000: // fallthrough
- case QNetworkConfiguration::BearerEVDO: // fallthrough
- case QNetworkConfiguration::BearerWCDMA: // fallthrough
- case QNetworkConfiguration::BearerHSPA: // fallthrough
- case QNetworkConfiguration::Bearer3G:
- return QNetworkConfiguration::Bearer3G;
- case QNetworkConfiguration::BearerWiMAX: // fallthrough
- case QNetworkConfiguration::BearerLTE: // fallthrough
- case QNetworkConfiguration::Bearer4G:
- return QNetworkConfiguration::Bearer4G;
- default:
- qWarning() << "unknown bearer type" << type;
- return QNetworkConfiguration::BearerUnknown;
- }
-}
-/*!
- Returns the type of bearer used by this network configuration as a string.
-
- The string is not translated and therefore cannot be shown to the user. The subsequent table
- shows the fixed mappings between BearerType and the bearer type name for known types. If the
- BearerType is unknown this function may return additional information if it is available;
- otherwise an empty string will be returned.
-
- \table
- \header
- \li BearerType
- \li Value
- \row
- \li BearerUnknown
- \li The session is based on an unknown or unspecified bearer type. The value of the
- string returned describes the bearer type.
- \row
- \li BearerEthernet
- \li Ethernet
- \row
- \li BearerWLAN
- \li WLAN
- \row
- \li Bearer2G
- \li 2G
- \row
- \li Bearer3G
- \li 3G
- \row
- \li Bearer4G
- \li 4G
- \row
- \li BearerCDMA2000
- \li CDMA2000
- \row
- \li BearerWCDMA
- \li WCDMA
- \row
- \li BearerHSPA
- \li HSPA
- \row
- \li BearerBluetooth
- \li Bluetooth
- \row
- \li BearerWiMAX
- \li WiMAX
- \row
- \li BearerEVDO
- \li EVDO
- \row
- \li BearerLTE
- \li LTE
- \endtable
-
- This function returns an empty string if this is an invalid configuration, a network
- configuration of type \l QNetworkConfiguration::ServiceNetwork or
- \l QNetworkConfiguration::UserChoice.
-
- \sa bearerType(), bearerTypeFamily()
-*/
-QString QNetworkConfiguration::bearerTypeName() const
-{
- if (!isValid())
- return QString();
-
- QMutexLocker locker(&d->mutex);
-
- if (d->type == QNetworkConfiguration::ServiceNetwork ||
- d->type == QNetworkConfiguration::UserChoice)
- return QString();
-
- switch (d->bearerType) {
- case BearerEthernet:
- return QStringLiteral("Ethernet");
- case BearerWLAN:
- return QStringLiteral("WLAN");
- case Bearer2G:
- return QStringLiteral("2G");
- case Bearer3G:
- return QStringLiteral("3G");
- case Bearer4G:
- return QStringLiteral("4G");
- case BearerCDMA2000:
- return QStringLiteral("CDMA2000");
- case BearerWCDMA:
- return QStringLiteral("WCDMA");
- case BearerHSPA:
- return QStringLiteral("HSPA");
- case BearerBluetooth:
- return QStringLiteral("Bluetooth");
- case BearerWiMAX:
- return QStringLiteral("WiMAX");
- case BearerEVDO:
- return QStringLiteral("EVDO");
- case BearerLTE:
- return QStringLiteral("LTE");
- case BearerUnknown:
- break;
- }
- return QStringLiteral("Unknown");
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
deleted file mode 100644
index 048abc2fc8..0000000000
--- a/src/network/bearer/qnetworkconfiguration.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKCONFIGURATION_H
-#define QNETWORKCONFIGURATION_H
-
-#include <QtNetwork/qtnetworkglobal.h>
-
-#include <QtCore/qshareddata.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmetatype.h>
-
-QT_BEGIN_NAMESPACE
-
-class QNetworkConfigurationPrivate;
-class Q_NETWORK_EXPORT QNetworkConfiguration
-{
-public:
- QNetworkConfiguration();
- QNetworkConfiguration(const QNetworkConfiguration& other);
- QNetworkConfiguration &operator=(QNetworkConfiguration &&other) noexcept { swap(other); return *this; }
- QNetworkConfiguration &operator=(const QNetworkConfiguration &other);
- ~QNetworkConfiguration();
-
- void swap(QNetworkConfiguration &other) noexcept { qSwap(d, other.d); }
-
- bool operator==(const QNetworkConfiguration &other) const;
- inline bool operator!=(const QNetworkConfiguration &other) const
- { return !operator==(other); }
-
- enum Type {
- InternetAccessPoint = 0,
- ServiceNetwork,
- UserChoice,
- Invalid
- };
-
- enum Purpose {
- UnknownPurpose = 0,
- PublicPurpose,
- PrivatePurpose,
- ServiceSpecificPurpose
- };
-
- enum StateFlag {
- Undefined = 0x0000001,
- Defined = 0x0000002,
- Discovered = 0x0000006,
- Active = 0x000000e
- };
- Q_DECLARE_FLAGS(StateFlags, StateFlag)
-
- enum BearerType {
- BearerUnknown,
- BearerEthernet,
- BearerWLAN,
- Bearer2G,
- BearerCDMA2000,
- BearerWCDMA,
- BearerHSPA,
- BearerBluetooth,
- BearerWiMAX,
- BearerEVDO,
- BearerLTE,
- Bearer3G,
- Bearer4G
- };
-
- StateFlags state() const;
- Type type() const;
- Purpose purpose() const;
-
- BearerType bearerType() const;
- BearerType bearerTypeFamily() const;
- QString bearerTypeName() const;
-
- QString identifier() const;
- bool isRoamingAvailable() const;
- QList<QNetworkConfiguration> children() const;
-
- QString name() const;
- bool isValid() const;
-
- int connectTimeout() const;
- bool setConnectTimeout(int timeout);
-
-private:
- friend class QNetworkConfigurationPrivate;
- friend class QNetworkConfigurationManager;
- friend class QNetworkConfigurationManagerPrivate;
- friend class QNetworkSessionPrivate;
- QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> d;
-};
-
-Q_DECLARE_SHARED(QNetworkConfiguration)
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QNetworkConfiguration)
-
-#endif // QNETWORKCONFIGURATION_H
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
deleted file mode 100644
index 96854fe831..0000000000
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKCONFIGURATIONPRIVATE_H
-#define QNETWORKCONFIGURATIONPRIVATE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworkconfiguration.h"
-
-#include <QtCore/qshareddata.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-typedef QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfigurationPrivatePointer;
-class QNetworkConfigurationPrivate : public QSharedData
-{
-public:
- QNetworkConfigurationPrivate() :
- type(QNetworkConfiguration::Invalid),
- purpose(QNetworkConfiguration::UnknownPurpose),
- bearerType(QNetworkConfiguration::BearerUnknown),
- isValid(false), roamingSupported(false),
- timeout(DefaultTimeout)
- {}
-
- mutable QRecursiveMutex mutex;
-
- QString name;
- QString id;
-
- QNetworkConfiguration::StateFlags state;
- QNetworkConfiguration::Type type;
- QNetworkConfiguration::Purpose purpose;
- QNetworkConfiguration::BearerType bearerType;
-
- bool isValid;
- bool roamingSupported;
- int timeout;
-
- static Q_CONSTEXPR int DefaultTimeout = 30000;
-
-private:
- Q_DISABLE_COPY_MOVE(QNetworkConfigurationPrivate)
-};
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QNetworkConfigurationPrivatePointer)
-
-#endif // QNETWORKCONFIGURATIONPRIVATE_H
diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp
deleted file mode 100644
index 324016d72a..0000000000
--- a/src/network/bearer/qnetworksession.cpp
+++ /dev/null
@@ -1,740 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworksession.h"
-#include "qnetworksession_p.h"
-#include "qbearerengine_p.h"
-
-#include <QEventLoop>
-#include <QMetaMethod>
-#include <QTimer>
-#include <QThread>
-
-#include "qnetworkconfigmanager_p.h"
-
-// for QNetworkSession::interface
-#ifdef interface
-# undef interface
-#endif
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QNetworkSession
-
- \brief The QNetworkSession class provides control over the system's access points
- and enables session management for cases when multiple clients access the same access point.
-
- \since 4.7
-
- \inmodule QtNetwork
- \ingroup network
-
- A QNetworkSession enables control over the system's network interfaces. The session's configuration
- parameter are determined via the QNetworkConfiguration object to which it is bound. Depending on the
- type of the session (single access point or service network) a session may be linked to one or more
- network interfaces. By means of \l{open()}{opening} and \l{close()}{closing} of network sessions
- a developer can start and stop the systems network interfaces. If the configuration represents
- multiple access points (see \l QNetworkConfiguration::ServiceNetwork) more advanced features such as roaming may be supported.
-
- QNetworkSession supports session management within the same process and depending on the platform's
- capabilities may support out-of-process sessions. If the same
- network configuration is used by multiple open sessions the underlying network interface is only terminated once
- the last session has been closed.
-
- \section1 Roaming
-
- Applications may connect to the preferredConfigurationChanged() signal in order to
- receive notifications when a more suitable access point becomes available.
- In response to this signal the application must either initiate the roaming via migrate()
- or ignore() the new access point. Once the session has roamed the
- newConfigurationActivated() signal is emitted. The application may now test the
- carrier and must either accept() or reject() it. The session will return to the previous
- access point if the roaming was rejected. The subsequent state diagram depicts the required
- state transitions.
-
- \image roaming-states.png
-
- Some platforms may distinguish forced roaming and application level roaming (ALR).
- ALR implies that the application controls (via migrate(), ignore(), accept() and reject())
- whether a network session can roam from one access point to the next. Such control is useful
- if the application maintains stateful socket connections and wants to control the transition from
- one interface to the next. Forced roaming implies that the system automatically roams to the next network without
- consulting the application. This has the advantage that the application can make use of roaming features
- without actually being aware of it. It is expected that the application detects that the underlying
- socket is broken and automatically reconnects via the new network link.
-
- If the platform supports both modes of roaming, an application indicates its preference
- by connecting to the preferredConfigurationChanged() signal. Connecting to this signal means that
- the application wants to take control over the roaming behavior and therefore implies application
- level roaming. If the client does not connect to the preferredConfigurationChanged(), forced roaming
- is used. If forced roaming is not supported the network session will not roam by default.
-
- Some applications may want to suppress any form of roaming altogether. Possible use cases may be
- high priority downloads or remote services which cannot handle a roaming enabled client. Clients
- can suppress roaming by connecting to the preferredConfigurationChanged() signal and answer each
- signal emission with ignore().
-
- \sa QNetworkConfiguration, QNetworkConfigurationManager
-*/
-
-/*!
- \enum QNetworkSession::State
-
- This enum describes the connectivity state of the session. If the session is based on a
- single access point configuration the state of the session is the same as the state of the
- associated network interface.
-
- \value Invalid The session is invalid due to an invalid configuration. This may
- happen due to a removed access point or a configuration that was
- invalid to begin with.
- \value NotAvailable The session is based on a defined but not yet discovered QNetworkConfiguration
- (see \l QNetworkConfiguration::StateFlag).
- \value Connecting The network session is being established.
- \value Connected The network session is connected. If the current process wishes to use this session
- it has to register its interest by calling open(). A network session
- is considered to be ready for socket operations if it isOpen() and connected.
- \value Closing The network session is in the process of being shut down.
- \value Disconnected The network session is not connected. The associated QNetworkConfiguration
- has the state QNetworkConfiguration::Discovered.
- \value Roaming The network session is roaming from one access point to another
- access point.
-*/
-
-/*!
- \enum QNetworkSession::SessionError
-
- This enum describes the session errors that can occur.
-
- \value UnknownSessionError An unidentified error occurred.
- \value SessionAbortedError The session was aborted by the user or system.
- \value RoamingError The session cannot roam to a new configuration.
- \value OperationNotSupportedError The operation is not supported for current configuration.
- \value InvalidConfigurationError The operation cannot currently be performed for the
- current configuration.
-*/
-
-/*!
- \enum QNetworkSession::UsagePolicy
- \since 5.0
-
- These flags allow the system to inform the application of network usage restrictions that
- may be in place.
-
- \value NoPolicy No policy in force, usage is unrestricted.
- \value NoBackgroundTrafficPolicy Background network traffic (not user initiated) should be avoided
- for example to save battery or data charges
-*/
-
-/*!
- \fn void QNetworkSession::stateChanged(QNetworkSession::State state)
-
- This signal is emitted whenever the state of the network session changes.
- The \a state parameter is the new state.
-
- \sa state()
-*/
-
-/*!
- \fn void QNetworkSession::error(QNetworkSession::SessionError error)
-
- This signal is emitted after an error occurred. The \a error parameter
- describes the error that occurred.
-
- \sa error(), errorString()
-*/
-
-/*!
- \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless)
-
- This signal is emitted when the preferred configuration/access point for the
- session changes. Only sessions which are based on service network configurations
- may emit this signal. \a config can be used to determine access point specific
- details such as proxy settings and \a isSeamless indicates whether roaming will
- break the sessions IP address.
-
- As a consequence to this signal the application must either start the roaming process
- by calling migrate() or choose to ignore() the new access point.
-
- If the roaming process is non-seamless the IP address will change which means that
- a socket becomes invalid. However seamless mobility can ensure that the local IP address
- does not change. This is achieved by using a virtual IP address which is bound to the actual
- link address. During the roaming process the virtual address is attached to the new link
- address.
-
- Some platforms may support the concept of Forced Roaming and Application Level Roaming (ALR).
- Forced roaming implies that the platform may simply roam to a new configuration without
- consulting applications. It is up to the application to detect the link layer loss and reestablish
- its sockets. In contrast ALR provides the opportunity to prevent the system from roaming.
- If this session is based on a configuration that supports roaming the application can choose
- whether it wants to be consulted (ALR use case) by connecting to this signal. For as long as this signal
- connection remains the session remains registered as a roaming stakeholder; otherwise roaming will
- be enforced by the platform.
-
- \sa migrate(), ignore(), QNetworkConfiguration::isRoamingAvailable()
-*/
-
-/*!
- \fn void QNetworkSession::newConfigurationActivated()
-
- This signal is emitted once the session has roamed to the new access point.
- The application may reopen its socket and test the suitability of the new network link.
- Subsequently it must either accept() or reject() the new access point.
-
- \sa accept(), reject()
-*/
-
-/*!
- \fn void QNetworkSession::opened()
-
- This signal is emitted when the network session has been opened.
-
- The underlying network interface will not be shut down as long as the session remains open.
- Note that this feature is dependent on \l{QNetworkConfigurationManager::SystemSessionSupport}{system wide session support}.
-*/
-
-/*!
- \fn void QNetworkSession::closed()
-
- This signal is emitted when the network session has been closed.
-*/
-
-/*!
- \fn void QNetworkSession::usagePoliciesChanged(QNetworkSession::UsagePolicies usagePolicies)
- \since 5.0
-
- This signal is emitted when the \a usagePolicies in force are changed by the system.
-*/
-
-/*!
- Constructs a session based on \a connectionConfig with the given \a parent.
-
- \sa QNetworkConfiguration
-*/
-QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig, QObject *parent)
- : QObject(parent), d(nullptr)
-{
- qRegisterMetaType<QNetworkSession::State>();
- qRegisterMetaType<QNetworkSession::SessionError>();
- qRegisterMetaType<QNetworkSession::UsagePolicies>();
-
- // invalid configuration
- if (!connectionConfig.identifier().isEmpty()) {
- auto priv = qNetworkConfigurationManagerPrivate();
- const auto engines = priv ? priv->engines() : QList<QBearerEngine *>();
- for (QBearerEngine *engine : engines) {
- if (engine->hasIdentifier(connectionConfig.identifier())) {
- d = engine->createSessionBackend();
- d->q = this;
- d->publicConfig = connectionConfig;
- d->syncStateWithInterface();
- connect(d, SIGNAL(quitPendingWaitsForOpened()), this, SIGNAL(opened()));
- connect(d, SIGNAL(error(QNetworkSession::SessionError)),
- this, SIGNAL(error(QNetworkSession::SessionError)));
- connect(d, SIGNAL(stateChanged(QNetworkSession::State)),
- this, SIGNAL(stateChanged(QNetworkSession::State)));
- connect(d, SIGNAL(closed()), this, SIGNAL(closed()));
- connect(d, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)),
- this, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)));
- connect(d, SIGNAL(newConfigurationActivated()),
- this, SIGNAL(newConfigurationActivated()));
- connect(d, SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
- this, SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)));
- break;
- }
- }
- }
-}
-
-/*!
- Frees the resources associated with the QNetworkSession object.
-*/
-QNetworkSession::~QNetworkSession()
-{
- delete d;
-}
-
-/*!
- Creates an open session which increases the session counter on the underlying network interface.
- The system will not terminate a network interface until the session reference counter reaches zero.
- Therefore an open session allows an application to register its use of the interface.
-
- As a result of calling open() the interface will be started if it is not connected/up yet.
- Some platforms may not provide support for out-of-process sessions. On such platforms the session
- counter ignores any sessions held by another process. The platform capabilities can be
- detected via QNetworkConfigurationManager::capabilities().
-
- Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired
- by connecting to the stateChanged(), opened() or error() signals.
-
- It is not a requirement to open a session in order to monitor the underlying network interface.
-
- \sa close(), stop(), isOpen()
-*/
-void QNetworkSession::open()
-{
- if (d)
- d->open();
- else
- emit error(InvalidConfigurationError);
-}
-
-/*!
- Waits until the session has been opened, up to \a msecs milliseconds. If the session has been opened, this
- function returns \c true; otherwise it returns \c false. In the case where it returns \c false, you can call error()
- to determine the cause of the error.
-
- The following example waits up to one second for the session to be opened:
-
- \snippet code/src_network_bearer_qnetworksession.cpp 0
-
- If \a msecs is -1, this function will not time out.
-
- \sa open(), error()
-*/
-bool QNetworkSession::waitForOpened(int msecs)
-{
- if (!d)
- return false;
-
- if (d->isOpen)
- return true;
-
- if (!(d->state == Connecting || d->state == Connected)) {
- return false;
- }
-
- QEventLoop loop;
- QObject::connect(d, SIGNAL(quitPendingWaitsForOpened()), &loop, SLOT(quit()));
- QObject::connect(this, SIGNAL(error(QNetworkSession::SessionError)), &loop, SLOT(quit()));
-
- //final call
- if (msecs >= 0)
- QTimer::singleShot(msecs, &loop, SLOT(quit()));
-
- // enter the event loop and wait for opened/error/timeout
- loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
-
- return d->isOpen;
-}
-
-/*!
- Decreases the session counter on the associated network configuration. If the session counter reaches zero
- the active network interface is shut down. This also means that state() will only change from \l Connected to
- \l Disconnected if the current session was the last open session.
-
- If the platform does not support out-of-process sessions calling this function does not stop the
- interface. In this case \l{stop()} has to be used to force a shut down.
- The platform capabilities can be detected via QNetworkConfigurationManager::capabilities().
-
- Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired
- by connecting to the stateChanged(), opened() or error() signals.
-
- \sa open(), stop(), isOpen()
-*/
-void QNetworkSession::close()
-{
- if (d)
- d->close();
-}
-
-/*!
- Invalidates all open sessions against the network interface and therefore stops the
- underlying network interface. This function always changes the session's state() flag to
- \l Disconnected.
-
- \sa open(), close()
-*/
-void QNetworkSession::stop()
-{
- if (d)
- d->stop();
-}
-
-/*!
- Returns the QNetworkConfiguration that this network session object is based on.
-
- \sa QNetworkConfiguration
-*/
-QNetworkConfiguration QNetworkSession::configuration() const
-{
- return d ? d->publicConfig : QNetworkConfiguration();
-}
-
-#ifndef QT_NO_NETWORKINTERFACE
-/*!
- Returns the network interface that is used by this session.
-
- This function only returns a valid QNetworkInterface when this session is \l Connected.
-
- The returned interface may change as a result of a roaming process.
-
- \sa state()
-*/
-QNetworkInterface QNetworkSession::interface() const
-{
- return d ? d->currentInterface() : QNetworkInterface();
-}
-#endif
-
-/*!
- Returns \c true if this session is open. If the number of all open sessions is greater than
- zero the underlying network interface will remain connected/up.
-
- The session can be controlled via open() and close().
-*/
-bool QNetworkSession::isOpen() const
-{
- return d ? d->isOpen : false;
-}
-
-/*!
- Returns the state of the session.
-
- If the session is based on a single access point configuration the state of the
- session is the same as the state of the associated network interface. Therefore
- a network session object can be used to monitor network interfaces.
-
- A \l QNetworkConfiguration::ServiceNetwork based session summarizes the state of all its children
- and therefore returns the \l Connected state if at least one of the service network's
- \l {QNetworkConfiguration::children()}{children()} configurations is active.
-
- Note that it is not required to hold an open session in order to obtain the network interface state.
- A connected but closed session may be used to monitor network interfaces whereas an open and connected
- session object may prevent the network interface from being shut down.
-
- \sa error(), stateChanged()
-*/
-QNetworkSession::State QNetworkSession::state() const
-{
- return d ? d->state : QNetworkSession::Invalid;
-}
-
-/*!
- Returns the type of error that last occurred.
-
- \sa state(), errorString()
-*/
-QNetworkSession::SessionError QNetworkSession::error() const
-{
- return d ? d->error() : InvalidConfigurationError;
-}
-
-/*!
- Returns a human-readable description of the last device error that
- occurred.
-
- \sa error()
-*/
-QString QNetworkSession::errorString() const
-{
- return d ? d->errorString() : tr("Invalid configuration.");
-}
-
-/*!
- Returns the value for property \a key.
-
- A network session can have properties attached which may describe the session in more details.
- This function can be used to gain access to those properties.
-
- The following property keys are guaranteed to be specified on all platforms:
-
- \table 80%
- \header
- \li Key \li Description
- \row
- \li ActiveConfiguration
- \li If the session \l isOpen() this property returns the identifier of the
- QNetworkConfiguration that is used by this session; otherwise an empty string.
-
- The main purpose of this key is to determine which Internet access point is used
- if the session is based on a \l{QNetworkConfiguration::ServiceNetwork}{ServiceNetwork}.
- The following code snippet highlights the difference:
- \snippet code/src_network_bearer_qnetworksession.cpp 1
- \row
- \li UserChoiceConfiguration
- \li If the session \l isOpen() and is bound to a QNetworkConfiguration of type
- UserChoice, this property returns the identifier of the QNetworkConfiguration that the
- configuration resolved to when \l open() was called; otherwise an empty string.
-
- The purpose of this key is to determine the real QNetworkConfiguration that the
- session is using. This key is different from \e ActiveConfiguration in that
- this key may return an identifier for either a
- \l {QNetworkConfiguration::ServiceNetwork}{service network} or a
- \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations,
- whereas \e ActiveConfiguration always returns identifiers to
- \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations.
- \row
- \li ConnectInBackground
- \li Setting this property to \e true before calling \l open() implies that the connection attempt
- is made but if no connection can be established, the user is not connsulted and asked to select
- a suitable connection. This property is not set by default and support for it depends on the platform.
-
- \row
- \li AutoCloseSessionTimeout
- \li If the session requires polling to keep its state up to date, this property holds
- the timeout in milliseconds before the session will automatically close. If the
- value of this property is -1 the session will not automatically close. This property
- is set to -1 by default.
-
- The purpose of this property is to minimize resource use on platforms that use
- polling to update the state of the session. Applications can set the value of this
- property to the desired timeout before the session is closed. In response to the
- closed() signal the network session should be deleted to ensure that all polling is
- stopped. The session can then be recreated once it is required again. This property
- has no effect for sessions that do not require polling.
- \endtable
-*/
-QVariant QNetworkSession::sessionProperty(const QString &key) const
-{
- if (!d || !d->publicConfig.isValid())
- return QVariant();
-
- if (key == QLatin1String("ActiveConfiguration"))
- return d->isOpen ? d->activeConfig.identifier() : QString();
-
- if (key == QLatin1String("UserChoiceConfiguration")) {
- if (!d->isOpen || d->publicConfig.type() != QNetworkConfiguration::UserChoice)
- return QString();
-
- if (d->serviceConfig.isValid())
- return d->serviceConfig.identifier();
- else
- return d->activeConfig.identifier();
- }
-
- return d->sessionProperty(key);
-}
-
-/*!
- Sets the property \a value on the session. The property is identified using
- \a key. Removing an already set property can be achieved by passing an
- invalid QVariant.
-
- Note that the \e UserChoiceConfiguration and \e ActiveConfiguration
- properties are read only and cannot be changed using this method.
-*/
-void QNetworkSession::setSessionProperty(const QString &key, const QVariant &value)
-{
- if (!d)
- return;
-
- if (key == QLatin1String("ActiveConfiguration") ||
- key == QLatin1String("UserChoiceConfiguration")) {
- return;
- }
-
- d->setSessionProperty(key, value);
-}
-
-/*!
- Instructs the session to roam to the new access point. The old access point remains active
- until the application calls accept().
-
- The newConfigurationActivated() signal is emitted once roaming has been completed.
-
- \sa accept()
-*/
-void QNetworkSession::migrate()
-{
- if (d)
- d->migrate();
-}
-
-/*!
- This function indicates that the application does not wish to roam the session.
-
- \sa migrate()
-*/
-void QNetworkSession::ignore()
-{
- if (d)
- d->ignore();
-}
-
-/*!
- Instructs the session to permanently accept the new access point. Once this function
- has been called the session may not return to the old access point.
-
- The old access point may be closed in the process if there are no other network sessions for it.
- Therefore any open socket that still uses the old access point
- may become unusable and should be closed before completing the migration.
-*/
-void QNetworkSession::accept()
-{
- if (d)
- d->accept();
-}
-
-/*!
- The new access point is not suitable for the application. By calling this function the
- session returns to the previous access point/configuration. This action may invalidate
- any socket that has been created via the not desired access point.
-
- \sa accept()
-*/
-void QNetworkSession::reject()
-{
- if (d)
- d->reject();
-}
-
-
-/*!
- Returns the amount of data sent in bytes; otherwise 0.
-
- This field value includes the usage across all open network
- sessions which use the same network interface.
-
- If the session is based on a service network configuration the number of
- sent bytes across all active member configurations are returned.
-
- This function may not always be supported on all platforms and returns 0.
- The platform capability can be detected via QNetworkConfigurationManager::DataStatistics.
-
- \note On some platforms this function may run the main event loop.
-*/
-quint64 QNetworkSession::bytesWritten() const
-{
- return d ? d->bytesWritten() : Q_UINT64_C(0);
-}
-
-/*!
- Returns the amount of data received in bytes; otherwise 0.
-
- This field value includes the usage across all open network
- sessions which use the same network interface.
-
- If the session is based on a service network configuration the number of
- sent bytes across all active member configurations are returned.
-
- This function may not always be supported on all platforms and returns 0.
- The platform capability can be detected via QNetworkConfigurationManager::DataStatistics.
-
- \note On some platforms this function may run the main event loop.
-*/
-quint64 QNetworkSession::bytesReceived() const
-{
- return d ? d->bytesReceived() : Q_UINT64_C(0);
-}
-
-/*!
- Returns the number of seconds that the session has been active.
-*/
-quint64 QNetworkSession::activeTime() const
-{
- return d ? d->activeTime() : Q_UINT64_C(0);
-}
-
-/*!
- Returns the network usage policies currently in force by the system.
-*/
-QNetworkSession::UsagePolicies QNetworkSession::usagePolicies() const
-{
- return d ? d->usagePolicies() : QNetworkSession::NoPolicy;
-}
-
-/*!
- \internal
- Change usage policies for unit testing.
- In normal use, the policies are published by the bearer plugin
-*/
-void QNetworkSessionPrivate::setUsagePolicies(QNetworkSession &session, QNetworkSession::UsagePolicies policies)
-{
- if (!session.d)
- return;
- session.d->setUsagePolicies(policies);
-}
-
-/*!
- \internal
-
- This function is required to detect whether the client wants to control
- the roaming process. If he connects to preferredConfigurationChanged() signal
- he intends to influence it. Otherwise QNetworkSession always roams
- without registering this session as a stakeholder in the roaming process.
-
- For more details check the Forced vs ALR roaming section in the QNetworkSession
- class description.
-*/
-void QNetworkSession::connectNotify(const QMetaMethod &signal)
-{
- QObject::connectNotify(signal);
-
- if (!d)
- return;
-
- //check for preferredConfigurationChanged() signal connect notification
- //This is not required on all platforms
- static const QMetaMethod preferredConfigurationChangedSignal =
- QMetaMethod::fromSignal(&QNetworkSession::preferredConfigurationChanged);
- if (signal == preferredConfigurationChangedSignal)
- d->setALREnabled(true);
-}
-
-/*!
- \internal
-
- This function is called when the client disconnects from the
- preferredConfigurationChanged() signal.
-
- \sa connectNotify()
-*/
-void QNetworkSession::disconnectNotify(const QMetaMethod &signal)
-{
- QObject::disconnectNotify(signal);
-
- if (!d)
- return;
-
- //check for preferredConfigurationChanged() signal disconnect notification
- //This is not required on all platforms
- static const QMetaMethod preferredConfigurationChangedSignal =
- QMetaMethod::fromSignal(&QNetworkSession::preferredConfigurationChanged);
- if (signal == preferredConfigurationChangedSignal)
- d->setALREnabled(false);
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qnetworksession.cpp"
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h
deleted file mode 100644
index 1b5ae9098b..0000000000
--- a/src/network/bearer/qnetworksession.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKSESSION_H
-#define QNETWORKSESSION_H
-
-#include <QtNetwork/qtnetworkglobal.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qstring.h>
-#include <QtNetwork/qnetworkinterface.h>
-#include <QtCore/qvariant.h>
-#include <QtNetwork/qnetworkconfiguration.h>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-#if defined(Q_OS_WIN) && defined(interface)
-#undef interface
-#endif
-
-#include <QtCore/qshareddata.h>
-QT_BEGIN_NAMESPACE
-
-class QNetworkSessionPrivate;
-class Q_NETWORK_EXPORT QNetworkSession : public QObject
-{
- Q_OBJECT
-
-public:
- enum State {
- Invalid = 0,
- NotAvailable,
- Connecting,
- Connected,
- Closing,
- Disconnected,
- Roaming
- };
-
- enum SessionError {
- UnknownSessionError = 0,
- SessionAbortedError,
- RoamingError,
- OperationNotSupportedError,
- InvalidConfigurationError
- };
-
- enum UsagePolicy {
- NoPolicy = 0,
- NoBackgroundTrafficPolicy = 1
- };
-
- Q_DECLARE_FLAGS(UsagePolicies, UsagePolicy)
-
- explicit QNetworkSession(const QNetworkConfiguration &connConfig, QObject *parent = nullptr);
- virtual ~QNetworkSession();
-
- bool isOpen() const;
- QNetworkConfiguration configuration() const;
-#ifndef QT_NO_NETWORKINTERFACE
- QNetworkInterface interface() const;
-#endif
-
- State state() const;
- SessionError error() const;
- QString errorString() const;
- QVariant sessionProperty(const QString &key) const;
- void setSessionProperty(const QString &key, const QVariant &value);
-
- quint64 bytesWritten() const;
- quint64 bytesReceived() const;
- quint64 activeTime() const;
-
- QNetworkSession::UsagePolicies usagePolicies() const;
-
- bool waitForOpened(int msecs = 30000);
-
-public Q_SLOTS:
- void open();
- void close();
- void stop();
-
- //roaming related slots
- void migrate();
- void ignore();
- void accept();
- void reject();
-
-Q_SIGNALS:
- void stateChanged(QNetworkSession::State);
- void opened();
- void closed();
- void error(QNetworkSession::SessionError);
- void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless);
- void newConfigurationActivated();
- void usagePoliciesChanged(QNetworkSession::UsagePolicies usagePolicies);
-
-protected:
- virtual void connectNotify(const QMetaMethod &signal) override;
- virtual void disconnectNotify(const QMetaMethod &signal) override;
-
-private:
- Q_DISABLE_COPY(QNetworkSession)
- friend class QNetworkSessionPrivate;
- QNetworkSessionPrivate *d;
-};
-
-QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkSession::State)
-Q_DECLARE_METATYPE(QNetworkSession::SessionError)
-Q_DECLARE_METATYPE(QNetworkSession::UsagePolicies)
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QNETWORKSESSION_H
diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h
deleted file mode 100644
index 7c1ff63b68..0000000000
--- a/src/network/bearer/qnetworksession_p.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNETWORKSESSIONPRIVATE_H
-#define QNETWORKSESSIONPRIVATE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworksession.h"
-#include "qnetworkconfiguration_p.h"
-#include "QtCore/qsharedpointer.h"
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject
-{
- Q_OBJECT
-
- friend class QNetworkSession;
-
-public:
- QNetworkSessionPrivate() : QObject(),
- state(QNetworkSession::Invalid), isOpen(false)
- {}
- virtual ~QNetworkSessionPrivate()
- {}
-
- //called by QNetworkSession constructor and ensures
- //that the state is immediately updated (w/o actually opening
- //a session). Also this function should take care of
- //notification hooks to discover future state changes.
- virtual void syncStateWithInterface() = 0;
-
-#ifndef QT_NO_NETWORKINTERFACE
- virtual QNetworkInterface currentInterface() const = 0;
-#endif
- virtual QVariant sessionProperty(const QString &key) const = 0;
- virtual void setSessionProperty(const QString &key, const QVariant &value) = 0;
-
- virtual void open() = 0;
- virtual void close() = 0;
- virtual void stop() = 0;
-
- virtual void setALREnabled(bool /*enabled*/) {}
- virtual void migrate() = 0;
- virtual void accept() = 0;
- virtual void ignore() = 0;
- virtual void reject() = 0;
-
- virtual QString errorString() const = 0; //must return translated string
- virtual QNetworkSession::SessionError error() const = 0;
-
- virtual quint64 bytesWritten() const = 0;
- virtual quint64 bytesReceived() const = 0;
- virtual quint64 activeTime() const = 0;
-
- virtual QNetworkSession::UsagePolicies usagePolicies() const = 0;
- virtual void setUsagePolicies(QNetworkSession::UsagePolicies) = 0;
-
- static void setUsagePolicies(QNetworkSession&, QNetworkSession::UsagePolicies); //for unit testing
-protected:
- inline QNetworkConfigurationPrivatePointer privateConfiguration(const QNetworkConfiguration &config) const
- {
- return config.d;
- }
-
- inline void setPrivateConfiguration(QNetworkConfiguration &config,
- const QNetworkConfigurationPrivatePointer &ptr) const
- {
- config.d = ptr;
- }
-
-Q_SIGNALS:
- //releases any pending waitForOpened() calls
- void quitPendingWaitsForOpened();
-
- void error(QNetworkSession::SessionError error);
- void stateChanged(QNetworkSession::State state);
- void closed();
- void newConfigurationActivated();
- void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless);
- void usagePoliciesChanged(QNetworkSession::UsagePolicies);
-
-protected:
- QNetworkSession *q;
-
- // The config set on QNetworkSession.
- QNetworkConfiguration publicConfig;
-
- // If publicConfig is a ServiceNetwork this is a copy of publicConfig.
- // If publicConfig is an UserChoice that is resolved to a ServiceNetwork this is the actual
- // ServiceNetwork configuration.
- QNetworkConfiguration serviceConfig;
-
- // This is the actual active configuration currently in use by the session.
- // Either a copy of publicConfig or one of serviceConfig.children().
- QNetworkConfiguration activeConfig;
-
- QNetworkSession::State state;
- bool isOpen;
-
- QRecursiveMutex mutex;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif // QNETWORKSESSIONPRIVATE_H
diff --git a/src/network/bearer/qsharednetworksession.cpp b/src/network/bearer/qsharednetworksession.cpp
deleted file mode 100644
index b3e9892f4b..0000000000
--- a/src/network/bearer/qsharednetworksession.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsharednetworksession_p.h"
-#include "qbearerengine_p.h"
-#include <QThreadStorage>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-QThreadStorage<QSharedNetworkSessionManager *> tls;
-
-inline QSharedNetworkSessionManager* sharedNetworkSessionManager()
-{
- QSharedNetworkSessionManager* rv = tls.localData();
- if (!rv) {
- rv = new QSharedNetworkSessionManager;
- tls.setLocalData(rv);
- }
- return rv;
-}
-
-struct DeleteLater {
- void operator()(QObject* obj) const
- {
- obj->deleteLater();
- }
-};
-
-template <typename Container>
-static void maybe_prune_expired(Container &c)
-{
- if (c.size() > 16) {
- for (auto it = c.cbegin(), end = c.cend(); it != end; /*erasing*/) {
- if (!it->second.lock())
- it = c.erase(it);
- else
- ++it;
- }
- }
-}
-
-QSharedPointer<QNetworkSession> QSharedNetworkSessionManager::getSession(const QNetworkConfiguration &config)
-{
- QSharedNetworkSessionManager *m = sharedNetworkSessionManager();
- maybe_prune_expired(m->sessions);
- auto &entry = m->sessions[config];
- //if already have a session, return it
- if (auto p = entry.toStrongRef())
- return p;
- //otherwise make one
- QSharedPointer<QNetworkSession> session(new QNetworkSession(config), DeleteLater{});
- entry = session;
- return session;
-}
-
-void QSharedNetworkSessionManager::setSession(const QNetworkConfiguration &config, QSharedPointer<QNetworkSession> session)
-{
- QSharedNetworkSessionManager *m = sharedNetworkSessionManager();
- m->sessions[config] = std::move(session);
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h
deleted file mode 100644
index f22f9eeacb..0000000000
--- a/src/network/bearer/qsharednetworksession_p.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSHAREDNETWORKSESSIONPRIVATE_H
-#define QSHAREDNETWORKSESSIONPRIVATE_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qnetworksession.h"
-#include "qnetworkconfiguration.h"
-#include <QSharedPointer>
-#include <QWeakPointer>
-#include <QMutex>
-
-#include <unordered_map>
-
-#ifndef QT_NO_BEARERMANAGEMENT
-
-QT_BEGIN_NAMESPACE
-
-namespace QtPrivate {
-struct NetworkConfigurationHash {
- using result_type = size_t;
- using argument_type = QNetworkConfiguration;
- size_t operator()(const QNetworkConfiguration &config) const noexcept
- {
- return std::hash<size_t>{}(size_t(config.type()) | (size_t(config.bearerType()) << 8) | (size_t(config.purpose()) << 16));
- }
-};
-}
-
-class QSharedNetworkSessionManager
-{
-public:
- static QSharedPointer<QNetworkSession> getSession(const QNetworkConfiguration &config);
- static void setSession(const QNetworkConfiguration &config, QSharedPointer<QNetworkSession> session);
-private:
- std::unordered_map<QNetworkConfiguration, QWeakPointer<QNetworkSession>, QtPrivate::NetworkConfigurationHash> sessions;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_BEARERMANAGEMENT
-
-#endif //QSHAREDNETWORKSESSIONPRIVATE_H
-
diff --git a/src/network/compat/removed_api.cpp b/src/network/compat/removed_api.cpp
new file mode 100644
index 0000000000..ceda117538
--- /dev/null
+++ b/src/network/compat/removed_api.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2023 LLC «V Kontakte»
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#define QT_NETWORK_BUILD_REMOVED_API
+
+#include "qtnetworkglobal.h"
+
+QT_USE_NAMESPACE
+
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
+
+#include "qhostinfo.h"
+
+// static
+int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member)
+{
+ const auto *r = receiver;
+ return lookupHost(name, r, member);
+}
+
+
+#include "qnetworkreply.h"
+
+QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+{
+ return rawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+{
+ return hasRawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+#include "qnetworkrequest.h"
+
+QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+{
+ return rawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+{
+ return hasRawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+#include "qnetworkcookie.h"
+
+QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+{
+ return parseCookies(qToByteArrayViewIgnoringNull(cookieString));
+}
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_NETWORK_REMOVED_SINCE(6, 7)
+
+#if QT_NETWORK_REMOVED_SINCE(6, 8)
+
+#if QT_CONFIG(dnslookup)
+# include "qdnslookup.h" // inlined API
+#endif
+#include "qnetworkrequest.h" // inlined API
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_NETWORK_REMOVED_SINCE(6, 8)
diff --git a/src/network/configure.cmake b/src/network/configure.cmake
new file mode 100644
index 0000000000..cda444b873
--- /dev/null
+++ b/src/network/configure.cmake
@@ -0,0 +1,386 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#### Inputs
+
+
+
+#### Libraries
+
+qt_find_package(WrapBrotli PROVIDED_TARGETS WrapBrotli::WrapBrotliDec MODULE_NAME network QMAKE_LIB brotli)
+qt_find_package(Libproxy PROVIDED_TARGETS PkgConfig::Libproxy MODULE_NAME network QMAKE_LIB libproxy)
+qt_find_package(GSSAPI PROVIDED_TARGETS GSSAPI::GSSAPI MODULE_NAME network QMAKE_LIB gssapi)
+qt_find_package(GLIB2 OPTIONAL_COMPONENTS GOBJECT PROVIDED_TARGETS GLIB2::GOBJECT MODULE_NAME core QMAKE_LIB gobject)
+qt_find_package(GLIB2 OPTIONAL_COMPONENTS GIO PROVIDED_TARGETS GLIB2::GIO MODULE_NAME core QMAKE_LIB gio)
+qt_find_package(WrapResolv PROVIDED_TARGETS WrapResolv::WrapResolv MODULE_NAME network QMAKE_LIB libresolv)
+
+#### Tests
+
+# getifaddrs
+qt_config_compile_test(getifaddrs
+ LABEL "getifaddrs()"
+ CODE
+"#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+ifaddrs *list;
+getifaddrs(&list);
+freeifaddrs(list);
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: use: unmapped library: network
+)
+
+# ipv6ifname
+qt_config_compile_test(ipv6ifname
+ LABEL "IPv6 ifname"
+ CODE
+"#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+char buf[IFNAMSIZ];
+if_nametoindex(\"eth0\");
+if_indextoname(1, buf);
+if_freenameindex(if_nameindex());
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: use: unmapped library: network
+)
+
+# linux-netlink
+qt_config_compile_test(linux_netlink
+ LABEL "Linux AF_NETLINK sockets"
+ CODE
+"#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/socket.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+struct rtattr rta = { };
+struct ifinfomsg ifi = {};
+struct ifaddrmsg ifa = {};
+struct ifa_cacheinfo ci;
+ci.ifa_prefered = ci.ifa_valid = 0;
+(void)RTM_NEWLINK; (void)RTM_NEWADDR;
+(void)IFLA_ADDRESS; (void)IFLA_IFNAME;
+(void)IFA_ADDRESS; (void)IFA_LABEL; (void)IFA_CACHEINFO;
+(void)(IFA_F_SECONDARY | IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_MANAGETEMPADDR);
+ /* END TEST: */
+ return 0;
+}
+")
+
+# res_setserver
+qt_config_compile_test(res_setservers
+ LABEL "res_setservers()"
+ LIBRARIES
+ WrapResolv::WrapResolv
+ CODE
+"#include <sys/types.h>
+#include <netinet/in.h>
+#include <resolv.h>
+int main()
+{
+ union res_sockaddr_union sa;
+ res_state s = nullptr;
+ res_setservers(s, &sa, 1);
+ return 0;
+}
+"
+)
+
+# sctp
+qt_config_compile_test(sctp
+ LABEL "SCTP support"
+ CODE
+"#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+sctp_initmsg sctpInitMsg;
+socklen_t sctpInitMsgSize = sizeof(sctpInitMsg);
+(void) socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
+(void) getsockopt(-1, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg, &sctpInitMsgSize);
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: use: unmapped library: network
+)
+
+# dtls
+qt_config_compile_test(dtls
+ LABEL "DTLS support in OpenSSL"
+ LIBRARIES
+ WrapOpenSSLHeaders::WrapOpenSSLHeaders
+ CODE
+"#include <openssl/ssl.h>
+#if defined(OPENSSL_NO_DTLS) || !defined(DTLS1_2_VERSION)
+# error OpenSSL without DTLS support
+#endif
+
+int main(void)
+{
+ /* BEGIN TEST: */
+ /* END TEST: */
+ return 0;
+}
+")
+
+# ocsp
+qt_config_compile_test(ocsp
+ LABEL "OCSP stapling support in OpenSSL"
+ LIBRARIES
+ WrapOpenSSLHeaders::WrapOpenSSLHeaders
+ CODE
+"#include <openssl/ssl.h>
+#include <openssl/ocsp.h>
+#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)
+# error OpenSSL without OCSP stapling
+#endif
+
+int main(void)
+{
+ /* BEGIN TEST: */
+ /* END TEST: */
+ return 0;
+}
+")
+
+# networklistmanager
+qt_config_compile_test(networklistmanager
+ LABEL "Network List Manager"
+ CODE
+"#include <netlistmgr.h>
+#include <ocidl.h>
+#include <wrl/client.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+using namespace Microsoft::WRL;
+ComPtr<INetworkListManager> networkListManager;
+ComPtr<IConnectionPoint> connectionPoint;
+ComPtr<IConnectionPointContainer> connectionPointContainer;
+networkListManager.As(&connectionPointContainer);
+connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, &connectionPoint);
+ /* END TEST: */
+ return 0;
+}
+"# FIXME: qmake: LIBS += -lOle32
+)
+
+
+
+#### Features
+
+qt_feature("getifaddrs" PUBLIC
+ LABEL "getifaddrs()"
+ CONDITION VXWORKS OR UNIX AND NOT QT_FEATURE_linux_netlink AND TEST_getifaddrs
+)
+qt_feature_definition("getifaddrs" "QT_NO_GETIFADDRS" NEGATE VALUE "1")
+qt_feature("ipv6ifname" PUBLIC
+ LABEL "IPv6 ifname"
+ CONDITION VXWORKS OR UNIX AND NOT QT_FEATURE_linux_netlink AND TEST_ipv6ifname
+)
+qt_feature_definition("ipv6ifname" "QT_NO_IPV6IFNAME" NEGATE VALUE "1")
+qt_feature("libresolv" PRIVATE
+ LABEL "libresolv"
+ CONDITION WrapResolv_FOUND
+ AUTODETECT UNIX
+)
+qt_feature("libproxy" PRIVATE
+ LABEL "libproxy"
+ AUTODETECT OFF
+ CONDITION Libproxy_FOUND
+)
+qt_feature("linux-netlink" PRIVATE
+ LABEL "Linux AF_NETLINK"
+ CONDITION LINUX AND NOT ANDROID AND TEST_linux_netlink
+)
+qt_feature("res_setservers" PRIVATE
+ LABEL "res_setservers()"
+ CONDITION QT_FEATURE_libresolv AND TEST_res_setservers
+)
+qt_feature("securetransport" PUBLIC
+ LABEL "SecureTransport"
+ CONDITION APPLE
+ DISABLE INPUT_ssl STREQUAL 'no'
+)
+qt_feature_definition("securetransport" "QT_SECURETRANSPORT")
+qt_feature("schannel" PUBLIC
+ LABEL "Schannel"
+ CONDITION WIN32
+ DISABLE INPUT_ssl STREQUAL 'no'
+)
+qt_feature_definition("schannel" "QT_SCHANNEL")
+qt_feature("ssl" PUBLIC
+ LABEL "SSL"
+ CONDITION QT_FEATURE_securetransport OR QT_FEATURE_openssl OR QT_FEATURE_schannel
+)
+qt_feature_definition("ssl" "QT_NO_SSL" NEGATE VALUE "1")
+qt_feature("dtls" PUBLIC
+ SECTION "Networking"
+ LABEL "DTLS"
+ PURPOSE "Provides a DTLS implementation"
+ CONDITION QT_FEATURE_openssl AND QT_FEATURE_udpsocket AND TEST_dtls
+)
+qt_feature("ocsp" PUBLIC
+ SECTION "Networking"
+ LABEL "OCSP-stapling"
+ PURPOSE "Provides OCSP stapling support"
+ CONDITION QT_FEATURE_openssl AND TEST_ocsp
+)
+qt_feature("sctp" PUBLIC
+ LABEL "SCTP"
+ AUTODETECT OFF
+ CONDITION TEST_sctp
+)
+qt_feature_definition("sctp" "QT_NO_SCTP" NEGATE VALUE "1")
+qt_feature("system-proxies" PRIVATE
+ LABEL "Use system proxies"
+)
+qt_feature("http" PUBLIC
+ SECTION "Networking"
+ LABEL "HTTP"
+ PURPOSE "Provides support for the Hypertext Transfer Protocol in QNetworkAccessManager."
+ CONDITION QT_FEATURE_thread
+)
+qt_feature_definition("http" "QT_NO_HTTP" NEGATE VALUE "1")
+qt_feature("udpsocket" PUBLIC
+ SECTION "Networking"
+ LABEL "QUdpSocket"
+ PURPOSE "Provides access to UDP sockets."
+)
+qt_feature_definition("udpsocket" "QT_NO_UDPSOCKET" NEGATE VALUE "1")
+qt_feature("networkproxy" PUBLIC
+ SECTION "Networking"
+ LABEL "QNetworkProxy"
+ PURPOSE "Provides network proxy support."
+)
+qt_feature_definition("networkproxy" "QT_NO_NETWORKPROXY" NEGATE VALUE "1")
+qt_feature("socks5" PUBLIC
+ SECTION "Networking"
+ LABEL "SOCKS5"
+ PURPOSE "Provides SOCKS5 support in QNetworkProxy."
+ CONDITION QT_FEATURE_networkproxy
+)
+qt_feature_definition("socks5" "QT_NO_SOCKS5" NEGATE VALUE "1")
+qt_feature("networkinterface" PUBLIC
+ SECTION "Networking"
+ LABEL "QNetworkInterface"
+ PURPOSE "Supports enumerating a host's IP addresses and network interfaces."
+ CONDITION NOT WASM
+)
+qt_feature_definition("networkinterface" "QT_NO_NETWORKINTERFACE" NEGATE VALUE "1")
+qt_feature("networkdiskcache" PUBLIC
+ SECTION "Networking"
+ LABEL "QNetworkDiskCache"
+ PURPOSE "Provides a disk cache for network resources."
+ CONDITION QT_FEATURE_temporaryfile
+)
+qt_feature_definition("networkdiskcache" "QT_NO_NETWORKDISKCACHE" NEGATE VALUE "1")
+qt_feature("brotli" PUBLIC
+ SECTION "Networking"
+ LABEL "Brotli Decompression Support"
+ PURPOSE "Support for downloading and decompressing resources compressed with Brotli through QNetworkAccessManager."
+ CONDITION WrapBrotli_FOUND
+)
+qt_feature_definition("brotli" "QT_NO_BROTLI" NEGATE VALUE "1")
+qt_feature("localserver" PUBLIC
+ SECTION "Networking"
+ LABEL "QLocalServer"
+ PURPOSE "Provides a local socket based server."
+ CONDITION QT_FEATURE_temporaryfile
+)
+qt_feature_definition("localserver" "QT_NO_LOCALSERVER" NEGATE VALUE "1")
+qt_feature("dnslookup" PUBLIC
+ SECTION "Networking"
+ LABEL "QDnsLookup"
+ PURPOSE "Provides API for DNS lookups."
+ CONDITION QT_FEATURE_thread AND NOT INTEGRITY
+)
+qt_feature("gssapi" PUBLIC
+ SECTION "Networking"
+ LABEL "GSSAPI"
+ PURPOSE "Enable SPNEGO authentication through GSSAPI"
+ CONDITION NOT WIN32 AND GSSAPI_FOUND
+)
+qt_feature_definition("gssapi" "QT_NO_GSSAPI" NEGATE VALUE "1")
+qt_feature("sspi" PUBLIC
+ SECTION "Networking"
+ LABEL "SSPI"
+ PURPOSE "Enable NTLM/SPNEGO authentication through SSPI"
+ CONDITION WIN32
+)
+qt_feature_definition("sspi" "QT_NO_SSPI" NEGATE VALUE "1")
+qt_feature("networklistmanager" PRIVATE
+ SECTION "Networking"
+ LABEL "Network List Manager"
+ PURPOSE "Use Network List Manager to keep track of network connectivity"
+ CONDITION WIN32 AND TEST_networklistmanager
+)
+qt_feature("topleveldomain" PUBLIC
+ SECTION "Networking"
+ LABEL "qIsEffectiveTLD()"
+ PURPOSE "Provides support for checking if a domain is a top level domain. If enabled, a binary dump of the Public Suffix List (http://www.publicsuffix.org, Mozilla License) is included. The data is used in QNetworkCookieJar."
+ AUTODETECT NOT WASM
+ DISABLE INPUT_publicsuffix STREQUAL "no"
+)
+qt_feature("publicsuffix-qt" PRIVATE
+ LABEL " Built-in publicsuffix database"
+ CONDITION QT_FEATURE_topleveldomain
+ ENABLE INPUT_publicsuffix STREQUAL "qt" OR INPUT_publicsuffix STREQUAL "all"
+ DISABLE INPUT_publicsuffix STREQUAL "system"
+)
+qt_feature("publicsuffix-system" PRIVATE
+ LABEL " System publicsuffix database"
+ CONDITION QT_FEATURE_topleveldomain
+ AUTODETECT LINUX
+ ENABLE INPUT_publicsuffix STREQUAL "system" OR INPUT_publicsuffix STREQUAL "all"
+ DISABLE INPUT_publicsuffix STREQUAL "qt"
+)
+
+qt_configure_add_summary_section(NAME "Qt Network")
+qt_configure_add_summary_entry(ARGS "getifaddrs")
+qt_configure_add_summary_entry(ARGS "ipv6ifname")
+qt_configure_add_summary_entry(ARGS "libproxy")
+qt_configure_add_summary_entry(
+ ARGS "linux-netlink"
+ CONDITION LINUX
+)
+qt_configure_add_summary_entry(
+ ARGS "securetransport"
+ CONDITION APPLE
+)
+qt_configure_add_summary_entry(
+ ARGS "schannel"
+ CONDITION WIN32
+)
+qt_configure_add_summary_entry(ARGS "dtls")
+qt_configure_add_summary_entry(ARGS "ocsp")
+qt_configure_add_summary_entry(ARGS "sctp")
+qt_configure_add_summary_entry(ARGS "system-proxies")
+qt_configure_add_summary_entry(ARGS "gssapi")
+qt_configure_add_summary_entry(ARGS "brotli")
+qt_configure_add_summary_entry(ARGS "topleveldomain")
+qt_configure_add_summary_entry(ARGS "publicsuffix-qt")
+qt_configure_add_summary_entry(ARGS "publicsuffix-system")
+qt_configure_end_summary_section() # end of "Qt Network" section
diff --git a/src/network/configure.json b/src/network/configure.json
deleted file mode 100644
index 6bc71469dc..0000000000
--- a/src/network/configure.json
+++ /dev/null
@@ -1,493 +0,0 @@
-{
- "module": "network",
- "depends": [
- "core"
- ],
- "testDir": "../../config.tests",
-
- "commandline": {
- "assignments": {
- "OPENSSL_PATH": "openssl.prefix"
- },
- "options": {
- "libproxy": "boolean",
- "openssl": { "type": "optionalString", "values": [ "no", "yes", "linked", "runtime" ] },
- "openssl-linked": { "type": "void", "name": "openssl", "value": "linked" },
- "openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" },
- "dtls": "boolean",
- "ocsp": "boolean",
- "sctp": "boolean",
- "securetransport": "boolean",
- "schannel": "boolean",
- "ssl": "boolean",
- "system-proxies": "boolean"
- }
- },
-
- "libraries": {
- "corewlan": {
- "label": "CoreWLan",
- "export": "",
- "test": {
- "lang": "objc++",
- "include": [ "CoreWLAN/CoreWLAN.h", "CoreWLAN/CWInterface.h" ],
- "main": "[CWInterface interfaceWithName:@\"en2\"];"
- },
- "sources": [
- "-framework CoreWLAN -framework Foundation"
- ]
- },
- "network": {
- "sources": [
- { "type": "makeSpec", "spec": "NETWORK" }
- ]
- },
- "libproxy": {
- "label": "libproxy",
- "test": {
- "main": [
- "pxProxyFactory *factory = px_proxy_factory_new();",
- "px_proxy_factory_get_proxies(factory, \"http://qt-project.org\");",
- "px_proxy_factory_free(factory);"
- ]
- },
- "headers": "proxy.h",
- "sources": [
- "-lproxy"
- ]
- },
- "openssl_headers": {
- "label": "OpenSSL Headers",
- "export": "openssl",
- "test": {
- "tail": [
- "#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10100000L",
- "# error OpenSSL >= 1.1.0 is required",
- "#endif",
- "#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)",
- "# error OpenSSL was reported as >= 1.1.0 but is missing required features, possibly it's libressl which is unsupported",
- "#endif"
- ]
- },
- "headers": [ "openssl/ssl.h", "openssl/opensslv.h" ],
- "sources": [
- {
- "comment": "placeholder for OPENSSL_PATH",
- "libs": ""
- }
- ]
- },
- "openssl": {
- "label": "OpenSSL",
- "test": {
- "inherit": "openssl_headers",
- "main": "SSL_free(SSL_new(0));"
- },
- "sources": [
- { "type": "openssl" },
- {
- "libs": "-lssleay32 -llibeay32 -lUser32 -lWs2_32 -lAdvapi32 -lGdi32",
- "condition": "config.win32"
- },
- {
- "libs": "-llibssl -llibcrypto -lUser32 -lWs2_32 -lAdvapi32 -lCrypt32",
- "condition": "config.msvc"
- },
- {
- "libs": "-lssl -lcrypto",
- "condition": "!config.msvc"
- }
- ]
- },
- "gssapi": {
- "label": "KRB5 GSSAPI Support",
- "test": {
- "head": [
- "#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))",
- "# include <TargetConditionals.h>",
- "# if defined(TARGET_OS_MAC) && TARGET_OS_MAC",
- "# include <GSS/GSS.h>",
- "# endif",
- "#else",
- "# include <gssapi/gssapi.h>",
- "#endif"
- ],
- "main": [
- "gss_ctx_id_t ctx;",
- "gss_context_time(nullptr, ctx, nullptr);"
- ]
- },
- "sources": [
- { "libs": "-framework GSS", "condition": "config.darwin" },
- { "type": "pkgConfig", "args": "krb5-gssapi" },
- "-lgssapi_krb5"
- ]
- }
- },
-
- "tests": {
- "getifaddrs": {
- "label": "getifaddrs()",
- "type": "compile",
- "test": {
- "include": [ "sys/types.h", "sys/socket.h", "net/if.h", "ifaddrs.h" ],
- "main": [
- "ifaddrs *list;",
- "getifaddrs(&list);",
- "freeifaddrs(list);"
- ]
- },
- "use": "network"
- },
- "ipv6ifname": {
- "label": "IPv6 ifname",
- "type": "compile",
- "test": {
- "include": [ "sys/types.h", "sys/socket.h", "net/if.h" ],
- "main": [
- "char buf[IFNAMSIZ];",
- "if_nametoindex(\"eth0\");",
- "if_indextoname(1, buf);",
- "if_freenameindex(if_nameindex());"
- ]
- },
- "use": "network"
- },
- "linux-netlink": {
- "label": "Linux AF_NETLINK sockets",
- "type": "compile",
- "test": {
- "include": [ "asm/types.h", "linux/netlink.h", "linux/rtnetlink.h", "sys/socket.h" ],
- "main": [
- "struct rtattr rta = { };",
- "struct ifinfomsg ifi = {};",
- "struct ifaddrmsg ifa = {};",
- "struct ifa_cacheinfo ci;",
- "ci.ifa_prefered = ci.ifa_valid = 0;",
- "(void)RTM_NEWLINK; (void)RTM_NEWADDR;",
- "(void)IFLA_ADDRESS; (void)IFLA_IFNAME;",
- "(void)IFA_ADDRESS; (void)IFA_LABEL; (void)IFA_CACHEINFO;",
- "(void)(IFA_F_SECONDARY | IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_MANAGETEMPADDR);"
- ]
- }
- },
- "sctp": {
- "label": "SCTP support",
- "type": "compile",
- "test": {
- "include": [ "sys/types.h", "sys/socket.h", "netinet/in.h", "netinet/sctp.h" ],
- "main": [
- "sctp_initmsg sctpInitMsg;",
- "socklen_t sctpInitMsgSize = sizeof(sctpInitMsg);",
- "(void) socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);",
- "(void) getsockopt(-1, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg, &sctpInitMsgSize);"
- ]
- },
- "use": "network"
- },
- "dtls": {
- "label": "DTLS support in OpenSSL",
- "type": "compile",
- "test": {
- "include": "openssl/ssl.h",
- "tail": [
- "#if defined(OPENSSL_NO_DTLS) || !defined(DTLS1_2_VERSION)",
- "# error OpenSSL without DTLS support",
- "#endif"
- ]
- },
- "use": "openssl"
- },
- "ocsp": {
- "label": "OCSP stapling support in OpenSSL",
- "type": "compile",
- "test": {
- "include": ["openssl/ssl.h", "openssl/ocsp.h"],
- "tail": [
- "#if defined(OPENSSL_NO_OCSP) || defined(OPENSSL_NO_TLSEXT)",
- "# error OpenSSL without OCSP stapling",
- "#endif"
- ]
- },
- "use": "openssl"
- },
- "netlistmgr": {
- "label": "Network List Manager",
- "type": "compile",
- "test": {
- "include": [ "netlistmgr.h", "wrl/client.h" ],
- "main": [
- "using namespace Microsoft::WRL;",
- "ComPtr<INetworkListManager> networkListManager;",
- "ComPtr<IConnectionPoint> connectionPoint;",
- "ComPtr<IConnectionPointContainer> connectionPointContainer;",
- "networkListManager.As(&connectionPointContainer);",
- "connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, &connectionPoint);"
- ],
- "qmake": "LIBS += -lOle32"
- }
- }
- },
-
- "features": {
- "corewlan": {
- "label": "CoreWLan",
- "condition": "libs.corewlan",
- "emitIf": "config.darwin",
- "output": [ "feature", "privateFeature" ]
- },
- "getifaddrs": {
- "label": "getifaddrs()",
- "condition": "tests.getifaddrs",
- "output": [ "feature" ]
- },
- "ipv6ifname": {
- "label": "IPv6 ifname",
- "condition": "tests.ipv6ifname",
- "output": [ "feature" ]
- },
- "libproxy": {
- "label": "libproxy",
- "autoDetect": false,
- "condition": "libs.libproxy",
- "output": [ "privateFeature" ]
- },
- "linux-netlink": {
- "label": "Linux AF_NETLINK",
- "condition": "config.linux && tests.linux-netlink",
- "output": [ "privateFeature" ]
- },
- "openssl": {
- "label": "OpenSSL",
- "enable": "false",
- "condition": "features.openssl-runtime || features.openssl-linked",
- "output": [
- "privateFeature",
- { "type": "publicQtConfig", "condition": "!features.openssl-linked" },
- { "type": "define", "negative": true, "name": "QT_NO_OPENSSL" }
- ]
- },
- "openssl-runtime": {
- "autoDetect": "!config.winrt && !config.wasm",
- "enable": "input.openssl == 'yes' || input.openssl == 'runtime'",
- "disable": "input.openssl == 'no' || input.openssl == 'linked' || input.ssl == 'no'",
- "condition": "!features.securetransport && !features.schannel && libs.openssl_headers"
- },
- "openssl-linked": {
- "label": " Qt directly linked to OpenSSL",
- "autoDetect": false,
- "enable": "input.openssl == 'linked'",
- "condition": "!features.securetransport && !features.schannel && libs.openssl",
- "output": [
- "privateFeature",
- { "type": "define", "name": "QT_LINKED_OPENSSL" }
- ]
- },
- "securetransport": {
- "label": "SecureTransport",
- "disable": "input.securetransport == 'no' || input.ssl == 'no'",
- "condition": "config.darwin && (input.openssl == '' || input.openssl == 'no')",
- "output": [
- "publicFeature",
- { "type": "define", "name": "QT_SECURETRANSPORT" }
- ]
- },
- "schannel": {
- "label": "Schannel",
- "disable": "input.schannel == 'no' || input.ssl == 'no'",
- "condition": "input.schannel == 'yes' && config.win32 && !config.winrt && (input.openssl == '' || input.openssl == 'no')",
- "output": [
- "publicFeature",
- { "type": "define", "name": "QT_SCHANNEL" }
- ]
- },
- "ssl": {
- "label": "SSL",
- "condition": "config.winrt || features.securetransport || features.openssl || features.schannel",
- "output": [ "publicFeature", "feature" ]
- },
- "dtls": {
- "label": "DTLS",
- "purpose": "Provides a DTLS implementation",
- "section": "Networking",
- "condition": "features.openssl && features.udpsocket && tests.dtls",
- "output": [ "publicFeature" ]
- },
- "ocsp": {
- "label": "OCSP-stapling",
- "purpose": "Provides OCSP stapling support",
- "section": "Networking",
- "condition": "features.opensslv11 && tests.ocsp",
- "output": [ "publicFeature" ]
- },
- "opensslv11": {
- "label": "OpenSSL 1.1",
- "condition": "features.openssl",
- "output": [ "publicFeature" ]
- },
- "sctp": {
- "label": "SCTP",
- "autoDetect": false,
- "condition": "tests.sctp",
- "output": [ "publicFeature", "feature" ]
- },
- "system-proxies": {
- "label": "Use system proxies",
- "output": [ "privateFeature" ]
- },
- "ftp": {
- "label": "FTP",
- "purpose": "Provides support for the File Transfer Protocol in QNetworkAccessManager.",
- "section": "Networking",
- "condition": "features.textdate",
- "output": [ "publicFeature", "feature" ]
- },
- "http": {
- "label": "HTTP",
- "purpose": "Provides support for the Hypertext Transfer Protocol in QNetworkAccessManager.",
- "section": "Networking",
- "condition": "features.thread",
- "output": [ "publicFeature", "feature" ]
- },
- "udpsocket": {
- "label": "QUdpSocket",
- "purpose": "Provides access to UDP sockets.",
- "section": "Networking",
- "output": [ "publicFeature", "feature" ]
- },
- "networkproxy": {
- "label": "QNetworkProxy",
- "purpose": "Provides network proxy support.",
- "section": "Networking",
- "output": [ "publicFeature", "feature" ]
- },
- "socks5": {
- "label": "SOCKS5",
- "purpose": "Provides SOCKS5 support in QNetworkProxy.",
- "section": "Networking",
- "condition": "features.networkproxy",
- "output": [ "publicFeature", "feature" ]
- },
- "networkinterface": {
- "label": "QNetworkInterface",
- "purpose": "Supports enumerating a host's IP addresses and network interfaces.",
- "condition": "!config.wasm",
- "section": "Networking",
- "output": [ "publicFeature", "feature" ]
- },
- "networkdiskcache": {
- "label": "QNetworkDiskCache",
- "purpose": "Provides a disk cache for network resources.",
- "section": "Networking",
- "condition": "features.temporaryfile",
- "output": [ "publicFeature", "feature" ]
- },
- "bearermanagement": {
- "label": "Bearer management",
- "purpose": "Provides bearer management for the network stack.",
- "section": "Networking",
- "condition": "features.thread && features.library && features.networkinterface && features.properties",
- "output": [
- "publicFeature",
- "feature",
- { "type": "define", "negative": true, "name": "QT_NO_BEARERMANAGEMENT" }
- ]
- },
- "localserver": {
- "label": "QLocalServer",
- "purpose": "Provides a local socket based server.",
- "section": "Networking",
- "condition": "features.temporaryfile",
- "output": [ "publicFeature", "feature" ]
- },
- "dnslookup": {
- "label": "QDnsLookup",
- "purpose": "Provides API for DNS lookups.",
- "section": "Networking",
- "output": [ "publicFeature" ]
- },
- "gssapi": {
- "label": "GSSAPI",
- "purpose": "Enable SPNEGO authentication through GSSAPI",
- "section": "Networking",
- "condition": "!config.win32 && libs.gssapi",
- "output": [ "publicFeature", "feature" ]
- },
- "sspi": {
- "label": "SSPI",
- "purpose": "Enable NTLM/SPNEGO authentication through SSPI",
- "section": "Networking",
- "condition": "config.win32 && !config.winrt",
- "output": [ "publicFeature", "feature" ]
- },
- "netlistmgr": {
- "label": "Network List Manager",
- "purpose": "Use Network List Manager to keep track of network connectivity",
- "section": "Networking",
- "condition": "config.win32 && tests.netlistmgr",
- "output": [ "privateFeature" ]
- }
- },
-
- "report": [
- {
- "type": "note",
- "condition": "features.openssl-linked && libs.openssl.source != 0
- && input.openssl.prefix == '' && input.openssl.libs == '' && input.openssl.libs.debug == ''",
- "message": "When linking against OpenSSL, you can override the default
-library names through OPENSSL_LIBS.
-For example:
- OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked"
- },
- {
- "type": "warning",
- "condition": "features.libproxy && input.qt_namespace == ''",
- "message": "Some of libproxy's plugins may use incompatible Qt versions.
-
- Some platforms and distributions ship libproxy with plugins, such
- as config_kde4.so, that are linked against old versions of Qt; and
- libproxy loads these plugins automatically when initialized. If Qt
- is not in a namespace, that loading causes a crash. Even if the
- systems on which you build and test have no such plugins, your
- users' systems may have them. We therefore recommend that you
- combine -libproxy with -qtnamespace when configuring Qt."
- }
- ],
-
- "summary": [
- {
- "section": "Qt Network",
- "entries": [
- {
- "type": "feature",
- "args": "corewlan",
- "condition": "config.darwin"
- },
- "getifaddrs", "ipv6ifname", "libproxy",
- {
- "type": "feature",
- "args": "linux-netlink",
- "condition": "config.linux"
- },
- {
- "type": "feature",
- "args": "securetransport",
- "condition": "config.darwin"
- },
- {
- "type": "feature",
- "args": "schannel",
- "condition": "config.win32 && !config.winrt"
- },
- "openssl",
- "openssl-linked",
- "opensslv11",
- "dtls",
- "ocsp",
- "sctp",
- "system-proxies",
- "gssapi"
- ]
- }
- ]
-}
diff --git a/src/network/configure.pri b/src/network/configure.pri
deleted file mode 100644
index ad4d711cba..0000000000
--- a/src/network/configure.pri
+++ /dev/null
@@ -1,13 +0,0 @@
-# custom tests
-
-defineTest(qtConfLibrary_openssl) {
- eval(libs = $$getenv("OPENSSL_LIBS"))
- !isEmpty(libs) {
- !qtConfResolveLibs($${1}.libs, $$libs): \
- return(false)
- return(true)
- }
- qtLog("$OPENSSL_LIBS is not set.")
- return(false)
-}
-
diff --git a/src/network/doc/images/network-examples.png b/src/network/doc/images/network-examples.png
deleted file mode 100644
index 15dfba850a..0000000000
--- a/src/network/doc/images/network-examples.png
+++ /dev/null
Binary files differ
diff --git a/src/network/doc/images/network-examples.webp b/src/network/doc/images/network-examples.webp
new file mode 100644
index 0000000000..4f6269ec8b
--- /dev/null
+++ b/src/network/doc/images/network-examples.webp
Binary files differ
diff --git a/src/network/doc/qtnetwork.qdocconf b/src/network/doc/qtnetwork.qdocconf
index 5465b1c0af..1e1162bdac 100644
--- a/src/network/doc/qtnetwork.qdocconf
+++ b/src/network/doc/qtnetwork.qdocconf
@@ -15,10 +15,6 @@ qhp.QtNetwork.virtualFolder = qtnetwork
qhp.QtNetwork.indexTitle = Qt Network
qhp.QtNetwork.indexRoot =
-qhp.QtNetwork.filterAttributes = qtnetwork $QT_VERSION qtrefdoc
-qhp.QtNetwork.customFilters.Qt.name = QtNetwork $QT_VERSION
-qhp.QtNetwork.customFilters.Qt.filterAttributes = qtnetwork $QT_VERSION
-
qhp.QtNetwork.subprojects = classes
qhp.QtNetwork.subprojects.classes.title = C++ Classes
qhp.QtNetwork.subprojects.classes.indexTitle = Qt Network C++ Classes
@@ -27,7 +23,7 @@ qhp.QtNetwork.subprojects.classes.sortPages = true
tagfile = ../../../doc/qtnetwork/qtnetwork.tags
-depends += qtcore qtgui qtdoc qmake
+depends += qtcore qtgui qtdoc qmake qtcmake qtwidgets
headerdirs += ..
@@ -41,7 +37,15 @@ exampledirs += ../../../examples/network \
imagedirs += images \
../../../examples/network/doc/images
-manifestmeta.highlighted.names = "QtNetwork/HTTP Example"
-
navigation.landingpage = "Qt Network"
navigation.cppclassespage = "Qt Network C++ Classes"
+
+manifestmeta.highlighted.names = \
+ "QtNetwork/Fortune Client" \
+ "QtNetwork/Fortune Server" \
+ "QtNetwork/HTTP Client" \
+ "QtNetwork/Secure Socket Client" \
+ "QtNetwork/Torrent Example"
+
+# Enforce zero documentation warnings
+warninglimit = 0
diff --git a/src/network/doc/snippets/CMakeLists.txt b/src/network/doc/snippets/CMakeLists.txt
new file mode 100644
index 0000000000..05e5668e24
--- /dev/null
+++ b/src/network/doc/snippets/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+project(network_cppsnippets)
+
+add_executable(mytarget
+ network/tcpwait.cpp
+)
+
+# ![0]
+find_package(Qt6 REQUIRED COMPONENTS Network)
+target_link_libraries(mytarget PRIVATE Qt6::Network)
+# ![0]
diff --git a/src/network/doc/snippets/code/doc_src_qtnetwork.cpp b/src/network/doc/snippets/code/doc_src_qtnetwork.cpp
deleted file mode 100644
index a74a1fce41..0000000000
--- a/src/network/doc/snippets/code/doc_src_qtnetwork.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [1]
-#include <QtNetwork>
-//! [1]
diff --git a/src/network/doc/snippets/code/src_network_access_qftp.cpp b/src/network/doc/snippets/code/src_network_access_qftp.cpp
deleted file mode 100644
index 8472477a01..0000000000
--- a/src/network/doc/snippets/code/src_network_access_qftp.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
-QFtp *ftp = new QFtp(parent);
-ftp->connectToHost("ftp.qt-project.org");
-ftp->login();
-//! [0]
-
-
-//! [1]
-ftp->connectToHost("ftp.qt-project.org"); // id == 1
-ftp->login(); // id == 2
-ftp->cd("qt"); // id == 3
-ftp->get("INSTALL"); // id == 4
-ftp->close(); // id == 5
-//! [1]
-
-
-//! [2]
-start(1)
-stateChanged(HostLookup)
-stateChanged(Connecting)
-stateChanged(Connected)
-finished(1, false)
-
-start(2)
-stateChanged(LoggedIn)
-finished(2, false)
-
-start(3)
-finished(3, false)
-
-start(4)
-dataTransferProgress(0, 3798)
-dataTransferProgress(2896, 3798)
-readyRead()
-dataTransferProgress(3798, 3798)
-readyRead()
-finished(4, false)
-
-start(5)
-stateChanged(Closing)
-stateChanged(Unconnected)
-finished(5, false)
-
-done(false)
-//! [2]
-
-
-//! [3]
-start(1)
-stateChanged(HostLookup)
-stateChanged(Connecting)
-stateChanged(Connected)
-finished(1, false)
-
-start(2)
-finished(2, true)
-
-done(true)
-//! [3]
diff --git a/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp b/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
index 4166adef23..73308b5547 100644
--- a/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
diff --git a/src/network/doc/snippets/code/src_network_access_qhttppart.cpp b/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
index e2a280fe22..059a4c60e9 100644
--- a/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
Content-Type: text/plain
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp
index aed74e308e..14eed532cb 100644
--- a/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
@@ -64,26 +17,8 @@ request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
QNetworkReply *reply = manager->get(request);
connect(reply, &QIODevice::readyRead, this, &MyClass::slotReadyRead);
-connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
+connect(reply, &QNetworkReply::errorOccurred,
this, &MyClass::slotError);
connect(reply, &QNetworkReply::sslErrors,
this, &MyClass::slotSslErrors);
//! [1]
-
-//! [2]
-QNetworkConfigurationManager manager;
-networkAccessManager->setConfiguration(manager.defaultConfiguration());
-//! [2]
-
-//! [3]
-networkAccessManager->setConfiguration(QNetworkConfiguration());
-//! [3]
-
-//! [4]
-networkAccessManager->setNetworkAccessible(QNetworkAccessManager::NotAccessible);
-//! [4]
-
-//! [5]
-networkAccessManager->setNetworkAccessible(QNetworkAccessManager::Accessible);
-//! [5]
-
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
index f5791abd41..1087f52035 100644
--- a/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
@@ -1,67 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
-diskCache->setCacheDirectory("cacheDir");
+QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ + QLatin1StringView("/cacheDir/");
+diskCache->setCacheDirectory(directory);
manager->setCache(diskCache);
//! [0]
//! [1]
+using namespace Qt::StringLiterals;
// do a normal request (preferred from network, as this is the default)
-QNetworkRequest request(QUrl(QString("http://qt-project.org")));
+QNetworkRequest request(QUrl(u"http://qt-project.org"_s));
manager->get(request);
// do a request preferred from cache
-QNetworkRequest request2(QUrl(QString("http://qt-project.org")));
+QNetworkRequest request2(QUrl(u"http://qt-project.org"_s));
request2.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
manager->get(request2);
//! [1]
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkreply.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkreply.cpp
index 3ac98302b6..4915b18f1e 100644
--- a/src/network/doc/snippets/code/src_network_access_qnetworkreply.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkreply.cpp
@@ -1,55 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+using namespace Qt::StringLiterals;
//! [0]
-QList<QSslCertificate> cert = QSslCertificate::fromPath(QLatin1String("server-certificate.pem"));
+QList<QSslCertificate> cert = QSslCertificate::fromPath("server-certificate.pem"_L1);
QSslError error(QSslError::SelfSignedCertificate, cert.at(0));
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error);
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkrequest.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkrequest.cpp
index 7b3efa812e..045e65dc05 100644
--- a/src/network/doc/snippets/code/src_network_access_qnetworkrequest.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkrequest.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
request.setRawHeader(QByteArray("Last-Modified"), QByteArray("Sun, 06 Nov 1994 08:49:37 GMT"));
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..f7994d442c
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+using namespace Qt::StringLiterals;
+
+//! [0]
+// Instantiate a factory somewhere suitable in the application
+QNetworkRequestFactory api{{"https://example.com/v1"_L1}};
+
+// Set bearer token
+api.setBearerToken("my_token");
+
+// Issue requests (reply handling omitted for brevity)
+manager.get(api.createRequest("models"_L1)); // https://example.com/v1/models
+// The conventional leading '/' for the path can be used as well
+manager.get(api.createRequest("/models"_L1)); // https://example.com/v1/models
+//! [0]
+
+
+//! [1]
+// Here the API version v2 is used as the base path:
+QNetworkRequestFactory api{{"https://example.com/v2"_L1}};
+// ...
+manager.get(api.createRequest("models"_L1)); // https://example.com/v2/models
+// Equivalent with a leading '/'
+manager.get(api.createRequest("/models"_L1)); // https://example.com/v2/models
+//! [1]
+
diff --git a/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp
new file mode 100644
index 0000000000..8f9e00f4b6
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+QNetworkReply *reply = manager->get(request);
+QObject::connect(reply, &QNetworkReply::finished, this, [reply]() {
+ // The reply may be wrapped in the finish handler:
+ QRestReply restReply(reply);
+ if (restReply.isSuccess())
+ // ...
+});
+//! [0]
+
+
+//! [1]
+// With lambda
+manager->get(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess()) {
+ // ...
+ }
+});
+// With member function
+manager->get(request, this, &MyClass::handleFinished);
+//! [1]
+
+
+//! [2]
+QJsonDocument myJson;
+// ...
+manager->post(request, myJson, this, [this](QRestReply &reply) {
+ if (!reply.isSuccess()) {
+ // ...
+ }
+ if (std::optional json = reply.readJson()) {
+ // use *json
+ }
+});
+//! [2]
+
+
+//! [3]
+manager->get(request, this, [this](QRestReply &reply) {
+ if (!reply.isSuccess())
+ // handle error
+ if (std::optional json = reply.readJson())
+ // use *json
+});
+//! [3]
+
+
+//! [4]
+manager->get(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [4]
+
+
+//! [5]
+manager->post(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [5]
+
+
+//! [6]
+manager->put(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [6]
+
+
+//! [7]
+manager->head(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [7]
+
+
+//! [8]
+manager->deleteResource(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [8]
+
+
+//! [9]
+manager->sendCustomRequest(request, "MYMETHOD", myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [9]
+
+
+//! [10]
+manager->patch(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [10]
diff --git a/src/network/doc/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp b/src/network/doc/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp
deleted file mode 100644
index 42992f08d9..0000000000
--- a/src/network/doc/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
-QNetworkConfigurationManager mgr;
-QList<QNetworkConfiguration> activeConfigs = mgr.allConfigurations(QNetworkConfiguration::Active);
-if (activeConfigs.count() > 0)
- Q_ASSERT(mgr.isOnline());
-else
- Q_ASSERT(!mgr.isOnline());
-//! [0]
diff --git a/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp b/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp
deleted file mode 100644
index b88b6d1768..0000000000
--- a/src/network/doc/snippets/code/src_network_bearer_qnetworksession.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
- session->open();
- if (session->waitForOpened(1000))
- qDebug("Open!");
-//! [0]
-
-//! [1]
- QNetworkConfigurationManager mgr;
- QNetworkConfiguration ap = mgr.defaultConfiguration();
- QNetworkSession *session = new QNetworkSession(ap);
- ... //code activates session
-
- QString ident = session->sessionProperty("ActiveConfiguration").toString();
- if ( ap.type() == QNetworkConfiguration::ServiceNetwork ) {
- Q_ASSERT( ap.identifier() != ident );
- Q_ASSERT( ap.children().contains( mgr.configurationFromIdentifier(ident) ) );
- } else if ( ap.type() == QNetworkConfiguration::InternetAccessPoint ) {
- Q_ASSERT( ap.identifier() == ident );
- }
- \endcode
-//! [1]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp b/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
index 76a0d61427..ce976001bb 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
@@ -1,60 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Jeremy Lainé <jeremy.laine@m4x.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
void MyObject::lookupServers()
{
// Create a DNS lookup.
dns = new QDnsLookup(this);
- connect(dns, SIGNAL(finished()),
- this, SLOT(handleServers()));
+ connect(dns, &QDnsLookup::finished, this, &MyObject::handleServers);
// Find the XMPP servers for gmail.com
dns->setType(QDnsLookup::SRV);
diff --git a/src/network/doc/snippets/code/src_network_kernel_qhostaddress.cpp b/src/network/doc/snippets/code/src_network_kernel_qhostaddress.cpp
index 60e2a8125d..1497fbd734 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qhostaddress.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qhostaddress.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
Q_IPV6ADDR addr = hostAddr.toIPv6Address();
diff --git a/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp b/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
index b7939bb1c0..7c39827b94 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
@@ -1,61 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
// To find the IP address of qt-project.org
-QHostInfo::lookupHost("qt-project.org",
- this, SLOT(printResults(QHostInfo)));
+QHostInfo::lookupHost("qt-project.org", this, &MyWidget::printResults);
// To find the host name for 4.2.2.1
-QHostInfo::lookupHost("4.2.2.1",
- this, SLOT(printResults(QHostInfo)));
+QHostInfo::lookupHost("4.2.2.1", this, &MyWidget::printResults);
//! [0]
@@ -65,8 +16,7 @@ QHostInfo info = QHostInfo::fromName("qt-project.org");
//! [2]
-QHostInfo::lookupHost("www.kde.org",
- this, SLOT(lookedUp(QHostInfo)));
+QHostInfo::lookupHost("www.kde.org", this, &MyWidget::lookedUp);
//! [2]
@@ -86,8 +36,7 @@ void MyWidget::lookedUp(const QHostInfo &host)
//! [4]
-QHostInfo::lookupHost("4.2.2.1",
- this, SLOT(lookedUp(QHostInfo)));
+QHostInfo::lookupHost("4.2.2.1", this, &MyWidget::lookedUp);
//! [4]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp
index f81ca97681..c0db73340d 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkdatagram.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
void Server::readPendingDatagrams()
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
index fc7fd7814a..d34750aaaf 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNetworkInterface::interfaceFromName(name).index()
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkproxy.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkproxy.cpp
index dd51fb1e5f..8e3e42f9ac 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qnetworkproxy.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkproxy.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNetworkProxy proxy;
diff --git a/src/network/doc/snippets/code/src_network_socket_qabstractsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qabstractsocket.cpp
index e19cb40666..2a568ff69f 100644
--- a/src/network/doc/snippets/code/src_network_socket_qabstractsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qabstractsocket.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
socket->connectToHost("imap", 143);
@@ -65,17 +18,5 @@ if (socket->state() == QAbstractSocket::UnconnectedState
//! [2]
- // This slot is connected to QAbstractSocket::readyRead()
- void SocketClass::readyReadSlot()
- {
- while (!socket.atEnd()) {
- QByteArray data = socket.read(100);
- ....
- }
- }
-//! [2]
-
-
-//! [3]
socket->setProxy(QNetworkProxy::NoProxy);
-//! [3]
+//! [2]
diff --git a/src/network/doc/snippets/code/src_network_socket_qlocalsocket_unix.cpp b/src/network/doc/snippets/code/src_network_socket_qlocalsocket_unix.cpp
index deafca831d..e6459a02d2 100644
--- a/src/network/doc/snippets/code/src_network_socket_qlocalsocket_unix.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qlocalsocket_unix.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
socket->connectToServer("market");
diff --git a/src/network/doc/snippets/code/src_network_socket_qnativesocketengine.cpp b/src/network/doc/snippets/code/src_network_socket_qnativesocketengine.cpp
index 8f7ff3bee4..4817ace4b1 100644
--- a/src/network/doc/snippets/code/src_network_socket_qnativesocketengine.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qnativesocketengine.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNativeSocketEngine socketLayer;
diff --git a/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp
index ac181f950c..8ab04c3acd 100644
--- a/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QSctpSocket *socket = new QSctpSocket(this);
diff --git a/src/network/doc/snippets/code/src_network_socket_qtcpserver.cpp b/src/network/doc/snippets/code/src_network_socket_qtcpserver.cpp
index a19c44c986..8c28cabf94 100644
--- a/src/network/doc/snippets/code/src_network_socket_qtcpserver.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qtcpserver.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
server->setProxy(QNetworkProxy::NoProxy);
diff --git a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
index f6a28ce46c..4666e28536 100644
--- a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
void Server::initSocket()
diff --git a/src/network/doc/snippets/code/src_network_ssl_qdtls.cpp b/src/network/doc/snippets/code/src_network_ssl_qdtls.cpp
index 2132b48338..cfdebec463 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qdtls.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qdtls.cpp
@@ -1,53 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+using namespace Qt::StringLiterals;
//! [0]
// A client initiates a handshake:
QUdpSocket clientSocket;
@@ -126,7 +80,7 @@ if (!dtls.doHandshake(&socket, dgram)) {
//! [5]
//! [6]
-QList<QSslCertificate> cert = QSslCertificate::fromPath(QLatin1String("server-certificate.pem"));
+QList<QSslCertificate> cert = QSslCertificate::fromPath("server-certificate.pem"_L1);
QSslError error(QSslError::SelfSignedCertificate, cert.at(0));
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error);
diff --git a/src/network/doc/snippets/code/src_network_ssl_qdtlscookie.cpp b/src/network/doc/snippets/code/src_network_ssl_qdtlscookie.cpp
index a9e596eca5..3cf7baf390 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qdtlscookie.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qdtlscookie.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
class DtlsServer : public QObject
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslcertificate.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslcertificate.cpp
index 12691da7a2..e80f014b4e 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslcertificate.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslcertificate.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-//! [0]
+//! [1]
const auto certs = QSslCertificate::fromPath("C:/ssl/certificate.*.pem",
- QSsl::Pem, QRegExp::Wildcard);
+ QSsl::Pem, QSslCertificate::Wildcard);
for (const QSslCertificate &cert : certs) {
qDebug() << cert.issuerInfo(QSslCertificate::Organization);
}
-//! [0]
+//! [1]
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
index 5d90dde5ea..d9a493d1ce 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
@@ -1,55 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QSslConfiguration config = sslSocket.sslConfiguration();
-config.setProtocol(QSsl::TlsV1_0);
+config.setProtocol(QSsl::TlsV1_2);
sslSocket.setSslConfiguration(config);
//! [0]
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
index 22e60840a3..b3242e057d 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
index c1b3ceaf69..eed032589e 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
@@ -1,56 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+using namespace Qt::StringLiterals;
//! [0]
QSslSocket *socket = new QSslSocket(this);
-connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));
+connect(socket, &QSslSocket::encrypted, this, &Receiver::ready);
socket->connectToHostEncrypted("imap.example.com", 993);
//! [0]
@@ -87,19 +42,13 @@ while (socket.waitForReadyRead())
//! [3]
QSslSocket socket;
-connect(&socket, SIGNAL(encrypted()), receiver, SLOT(socketEncrypted()));
+connect(&socket, &QSslSocket::encrypted, receiver, &Receiver::socketEncrypted);
socket.connectToHostEncrypted("imap", 993);
socket->write("1 CAPABILITY\r\n");
//! [3]
-//! [4]
-QSslSocket socket;
-socket.setCiphers("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA");
-//! [4]
-
-
//! [5]
socket->connectToHostEncrypted("imap", 993);
if (socket->waitForEncrypted(1000))
@@ -107,7 +56,7 @@ if (socket->waitForEncrypted(1000))
//! [5]
//! [6]
-QList<QSslCertificate> cert = QSslCertificate::fromPath(QLatin1String("server-certificate.pem"));
+QList<QSslCertificate> cert = QSslCertificate::fromPath("server-certificate.pem"_L1);
QSslError error(QSslError::SelfSignedCertificate, cert.at(0));
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error);
diff --git a/src/network/doc/snippets/network/CMakeLists.txt b/src/network/doc/snippets/network/CMakeLists.txt
new file mode 100644
index 0000000000..b75aeafea0
--- /dev/null
+++ b/src/network/doc/snippets/network/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+add_library(network_cppsnippets OBJECT tcpwait.cpp)
+
+target_link_libraries(network_cppsnippets PRIVATE
+ Qt::Network
+)
diff --git a/src/network/doc/snippets/code/doc_src_qtnetwork.pro b/src/network/doc/snippets/network/network.pro
index a100943e58..a100943e58 100644
--- a/src/network/doc/snippets/code/doc_src_qtnetwork.pro
+++ b/src/network/doc/snippets/network/network.pro
diff --git a/src/network/doc/snippets/network/tcpwait.cpp b/src/network/doc/snippets/network/tcpwait.cpp
index 97856e157f..b3afa84a8a 100644
--- a/src/network/doc/snippets/network/tcpwait.cpp
+++ b/src/network/doc/snippets/network/tcpwait.cpp
@@ -1,62 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QtGui>
#include <QTcpSocket>
-#include "server.h"
-
-int main(int argv, char **args)
+int test_tcpwait()
{
- QCoreApplication app(argv, args);
-
QTcpSocket socket;
socket.connectToHost("localhost", 1025);
@@ -74,6 +22,5 @@ int main(int argv, char **args)
break;
}
//! [0]
-
- return app.exec();
+ return numReadTotal;
}
diff --git a/src/network/doc/src/bearermanagement.qdoc b/src/network/doc/src/bearermanagement.qdoc
deleted file mode 100644
index f8d6a807d2..0000000000
--- a/src/network/doc/src/bearermanagement.qdoc
+++ /dev/null
@@ -1,240 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
-\page bearer-management.html
-
-\title Bearer Management
-\brief An API to control the system's connectivity state.
-
-Bearer Management controls the connectivity state of the system so that
-the user can start or stop interfaces or roam transparently between
-access points.
-
-\tableofcontents
-
-
-\section1 Overview
-
-The Bearer Management API controls the system's connectivity state. This
-incorporates simple information such as whether the device is online and
-how many interfaces there are as well as enables the application developer
-to start, stop network interfaces and influences other connection specific
-details. Depending on the platform's capabilities it may even provide
-session management so that a network interface remains up for as long as
-clients have a registered interest in them while at the same time
-optimizes the interface's uptime.
-
-This API does not provide support for management of network configurations
-themselves. It is up to the platform to provide infrastructure which
-enables to user to create, edit or delete network configurations.
-
-\section2 The API in Detail
-
-Computer systems manage their network interfaces via a set of configurations.
-Each configuration describes a set of parameters which instruct the system
-how a particular network interface is started. One of the most simplistic
-examples might be an Ethernet configuration that links a network card to a
-DHCP server. A more complex example might be a Wireless LAN configuration
-which may comprise of hardware details such as the WLAN card address,
-WLAN access point details (e.g ESSID, encryption details) and user specific
-information (for example username and password). Once the network interface
-was configured and started according to the configuration blue print,
-multiple applications are free to use this link layer connection/session
-for their own socket operations. Note that the QNetworkConfiguration object
-only provides limited information about the configuration details themselves.
-It's main purpose is to act as a configuration identifier through which link
-layer connections can be created, destroyed and monitored.
-
-QNetworkSession provides two types of use cases. It enables the monitoring of
-physical network interfaces and management of network sessions. Network sessions
-are a common feature on mobile devices where multiple applications
-can request network sessions as they see fit. The system consolidates and tracks
-active network sessions for the same network interface by maintaining the link
-layer connections until the last session has been closed. The subsequent table
-lists the major QNetworkSession functions and how they fit into the session and
-hardware management categories:
-
-\table 60%
-\header \li Interface management \li Session management
-\row \li QNetworkSession::stop() \li QNetworkSession::open()
-\row \li QNetworkSession::interface() \li QNetworkSession::close()
-\row \li QNetworkSession::state() \li QNetworkSession::isOpen()
-\row \li QNetworkSession::bytesWritten() \li QNetworkSession::migrate()
-\row \li QNetworkSession::bytesReceived() \li QNetworkSession::ignore()
-\row \li QNetworkSession::activeTime() \li QNetworkSession::accept()
-\row \li QNetworkSession::stateChanged() \li QNetworkSession::reject()
-\row \li \li QNetworkSession::opened()
-\row \li \li QNetworkSession::closed()
-\endtable
-
-The state of the session represents the state of the underlying access point
-whereas the session's openness implies the networking/connectivity state available
-to the current process.
-
-Possible use cases for interface management are network management related
-applications which intend to monitor the connectivity state but do not engage
-in network communication themselves. Any application wanting to open a socket
-to a remote address will typically use session management related functionality.
-
-\section3 Service networks
-
-Some mobile platforms use the concept of grouped access points (also
-called SNAP or Service Network Access Point). In principle multiple
-configurations are grouped together and possibly even prioritized when
-compared to each other. This is useful for use cases where all
-configurations serve a similar purpose or context. A common context could
-be that they provide access to the public Internet or possibly only to the
-office Intranet. By providing a pool of configurations the system can make
-a decision based on given priorities which usually map to factors such as
-speed, availability and cost. Furthermore the system can automatically
-roam from one access point to the next one while ensuring minimal impact on
-the user experience.
-
-The \l{QNetworkConfiguration::Type} flag specifies to what category a
-configuration belongs. The \l{QNetworkConfiguration::InternetAccessPoint}
-type is the most common example. It represents a configuration that can be
-used to create a session. The above mentioned grouping behavior is provided
-by \l {QNetworkConfiguration::ServiceNetwork} configurations. Service
-networks are place holders until such time when the user attempts to
-\l {QNetworkSession::open()}{open()} a new session. At that point in time
-the system determines which of the configurations \l{QNetworkConfiguration::children()}
-is best to use. The selection algorithm is provided by the platform and is usually managed
-by network settings applications. A service network can only have one level of indirection
-which implies children can only be of type \l {QNetworkConfiguration::InternetAccessPoint}.
-
-Most systems allow the user to define the systems default configuration.
-Usually the default behavior is either a service network, a particular
-Internet access point or the user instructs the platform to ask the user
-once an application requests the network. User interaction is generally
-implemented by some sort of system dialog which shows up at the appropriate
-point in time. The application does not have to handle the user input. This
-API provides the \l QNetworkConfigurationManager::defaultConfiguration()
-call which serves a similar purpose. The subsequent code snippet provides
-a quick way how an application can quickly create a new network session
-without (or only minimal) user interaction:
-
-\code
- // Set Internet Access Point
- QNetworkConfigurationManager manager;
- const bool canStartIAP = (manager.capabilities()
- & QNetworkConfigurationManager::CanStartAndStopInterfaces);
- // Is there default access point, use it
- QNetworkConfiguration cfg = manager.defaultConfiguration();
- if (!cfg.isValid() || (!canStartIAP && cfg.state() != QNetworkConfiguration::Active)) {
- QMessageBox::information(this, tr("Network"), tr(
- "No Access Point found."));
- return;
- }
-
- session = new QNetworkSession(cfg, this);
- session->open();
- session->waitForOpened(-1);
-\endcode
-
-To accommodate the "Ask user" use case the default configuration can be of
-type QNetworkConfiguration::UserChoice. A user choice configuration is
-resolved as part of the \l {QNetworkSession::open()} call. Note that a
-\l{QNetworkConfiguration::UserChoice}{UserChoice} configuration is only
-ever returned via \l {QNetworkConfigurationManager::defaultConfiguration()}
-and not \l QNetworkConfigurationManager::allConfigurations().
-
-On systems which do not maintain a list of
-\l {QNetworkConfigurationManager::defaultConfiguration()}{defaultConfiguration()}
-an invalid configuration is returned. A possible workaround could be to
-implement a custom dialog which is populated based on what
-\l QNetworkConfigurationManager::allConfigurations() returns.
-
-\section3 Managing network sessions
-
-A QNetworkSession object separates a \l {QNetworkSession::state()}{state()}
-and an \l{QNetworkSession::isOpen()}{isOpen()} condition.
-
-The state() attribute enables developers to detect whether the system
-currently maintains a global network session for the given
-QNetworkConfiguration. If \l {QNetworkSession::isOpen()}{isOpen()}
-returns \c true the QNetworkSession instance at hand was at least one of the
-entities requesting the global network session. This distinction is
-required to support the notion of session registrations. For as long as
-there are one or more open QNetworkSession instances the underlying
-network interface is not shut down. Therefore the session
-\l{QNetworkSession::state()}{state()} can be used to monitor the state of
-network interfaces.
-
-An open session is created by calling \l {QNetworkSession::open()} and
-closed via \l{QNetworkSession::close()}, respectively. If the session
-is \l{QNetworkSession::Disconnected}{disconnected} at the time of the
-\l{QNetworkSession::open()}{open()} call the underlying interface is started;
-otherwise only the reference counter against the global session is
-incremented. The opposite behavior can be observed when using
-\l{QNetworkSession::close()}{close()}.
-
-In some use cases it may be necessary to turn the interface off despite of
-open sessions. This can be achieved by calling
-\l{QNetworkSession::stop()}{stop()}. An example use case could be a
-network manager type of application allowing the user to control the
-overall state of the devices connectivity.
-
-Global (inter-process) session support is platform dependent and can be
-detected via \l {QNetworkConfigurationManager::SystemSessionSupport}.
-If the system does not support global session calling
-\l{QNetworkSession::close()}{close()} never stops the interface.
-
-\section3 Roaming
-
-Roaming is the process of reconnecting a device from one network to another
-while minimizing the impact on the application. The system notifies the application
-about link layer changes so that the required preparation can be taken.
-The most common reaction would be to reinitialize sockets and to renegotiate
-stateful connections with other parties. In the most extreme cases applications
-may even prevent the roaming altogether.
-
-Roaming is initiated when the system determines that a more appropriate access point
-becomes available to the user. In general such a decision is based on cost, network speed
-or network type (access to certain private networks may only be provided via certain access points).
-Almost all devices providing roaming support have some form of global configuration application
-enabling the user to define such groups of access points (service networks) and priorities.
-
-This API supports two types of roaming. Application level roaming (ALR)
-provides the most control over the process. Applications will be notified about upcoming
-link layer changes and get the opportunity to test the new access point. Eventually they can
-reject or accept the link layer change. The second form of roaming is referred to as Forced Roaming.
-The system simply changes the link layer without consulting the application. It is up to
-the application to detect that some of its internal socket may have become invalid. As a consequence
-it has to reinitialize those sockets and reestablish the previous user session without
-any interruption. Forced roaming has the advantage that applications don't have to
-manage the entire roaming process by themselves.
-
-QNetworkSession is the central class for managing roaming related issues.
-
-\section3 Platform capabilities
-
-Some API features are not available on all platforms. The
-\l QNetworkConfigurationManager::Capability should be used to detect
-platform features at runtime.
-
-*/
diff --git a/src/network/doc/src/dontdocument.qdoc b/src/network/doc/src/dontdocument.qdoc
index fe2e54b34c..eb0f8eb07e 100644
--- a/src/network/doc/src/dontdocument.qdoc
+++ b/src/network/doc/src/dontdocument.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\dontdocument (QTypeInfo QMetaTypeId QIPv6Address)
diff --git a/src/network/doc/src/examples.qdoc b/src/network/doc/src/examples.qdoc
index 3d31e04989..ee9084c74c 100644
--- a/src/network/doc/src/examples.qdoc
+++ b/src/network/doc/src/examples.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\group examples-network
@@ -31,7 +7,7 @@
\title Network Examples
\brief How to do network programming in Qt.
- \image network-examples.png
+ \image network-examples.webp
Qt is provided with an extensive set of network classes to support both
client-based and server side network programming.
@@ -46,19 +22,12 @@
\li \l{network/blockingfortuneclient}{Blocking Fortune Client}\raisedaster
\li \l{network/broadcastreceiver}{Broadcast Receiver}
\li \l{network/broadcastsender}{Broadcast Sender}
- \li \l{network/download}{Download}
- \li \l{network/downloadmanager}{Download Manager}
\li \l{network/network-chat}{Network Chat}
\li \l{network/fortuneclient}{Fortune Client}\raisedaster
\li \l{network/fortuneserver}{Fortune Server}\raisedaster
- \li \l{network/qftp}{FTP}\raisedaster
\li \l{network/http}{HTTP}
- \li \l{network/loopback}{Loopback}
\li \l{network/threadedfortuneserver}{Threaded Fortune Server}\raisedaster
\li \l{network/torrent}{Torrent}
- \li \l{network/googlesuggest}{Google Suggest}
- \li \l{network/bearercloud}{Bearer Cloud}\raisedaster
- \li \l{network/bearermonitor}{Bearer Monitor}
\li \l{network/securesocketclient}{Secure Socket Client}
\li \l{network/multicastreceiver}{Multicast Receiver}
\li \l{network/multicastsender}{Multicast Sender}
diff --git a/src/network/doc/src/external-resources.qdoc b/src/network/doc/src/external-resources.qdoc
index f033ddc729..60680cde0a 100644
--- a/src/network/doc/src/external-resources.qdoc
+++ b/src/network/doc/src/external-resources.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\externalpage https://www.openssl.org/
diff --git a/src/network/doc/src/network-programming.qdoc b/src/network/doc/src/network-programming.qdoc
index ce99af034b..d301ad01a3 100644
--- a/src/network/doc/src/network-programming.qdoc
+++ b/src/network/doc/src/network-programming.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\group network
@@ -43,9 +19,6 @@
QTcpServer and QUdpSocket that represent low level network concepts,
and high level classes such as QNetworkRequest, QNetworkReply and
QNetworkAccessManager to perform network operations using common protocols.
- It also offers classes such as QNetworkConfiguration,
- QNetworkConfigurationManager and QNetworkSession that implement bearer
- management.
\tableofcontents
@@ -54,7 +27,7 @@
The \l{Qt Network C++ Classes} page contains a list of the C++ classes
in Qt Network.
- \section1 High Level Network Operations for HTTP and FTP
+ \section1 High Level Network Operations for HTTP
The Network Access API is a collection of classes for performing
common network operations. The API provides an abstraction layer
@@ -67,7 +40,7 @@
with a request, such as any header information and the encryption
used. The URL specified when a request object is constructed
determines the protocol used for a request.
- Currently HTTP, FTP and local file URLs are supported for uploading
+ Currently HTTP and local file URLs are supported for uploading
and downloading.
The coordination of network operations is performed by the
@@ -261,29 +234,4 @@
by passing a factory to QNetworkProxyFactory::setApplicationProxyFactory()
and a custom proxying policy can be created by subclassing
QNetworkProxyFactory; see the class documentation for details.
-
- \section1 Bearer Management Support
-
- Bearer Management controls the connectivity state of the device such that
- the application can start or stop network interfaces and roam
- transparently between access points.
-
- The QNetworkConfigurationManager class manages the list of network
- configurations known to the device. A network configuration describes the
- set of parameters used to start a network interface and is represented by
- the QNetworkConfiguration class.
-
- A network interface is started by openning a QNetworkSession based on a
- given network configuration. In most situations creating a network session
- based on the platform specified default network configuration is
- appropriate. The default network configuration is returned by the
- QNetworkConfigurationManager::defaultConfiguration() function.
-
- On some platforms it is a platform requirement that the application open a
- network session before any network operations can be performed. This can be
- tested by the presents of the
- QNetworkConfigurationManager::NetworkSessionRequired flag in the value
- returned by the QNetworkConfigurationManager::capabilities() function.
-
- \sa {Bearer Management}
*/
diff --git a/src/network/doc/src/qt6-changes.qdoc b/src/network/doc/src/qt6-changes.qdoc
new file mode 100644
index 0000000000..3adce84760
--- /dev/null
+++ b/src/network/doc/src/qt6-changes.qdoc
@@ -0,0 +1,185 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page network-changes-qt6.html
+ \title Changes to Qt Network
+ \ingroup changes-qt-5-to-6
+ \brief Migrate Qt Network to Qt 6.
+
+ Qt 6 is a result of the conscious effort to make the framework more
+ efficient and easy to use.
+
+ We try to maintain binary and source compatibility for all the public
+ APIs in each release. But some changes were inevitable in an effort to
+ make Qt a better framework.
+
+ In this topic we summarize those changes in Qt Network, and provide
+ guidance to handle them.
+
+ \section1 API changes
+
+ \section2 Ambiguous name overloads
+
+ Several ambiguous overloaded functions are removed. The error() signal
+ is replaced by errorOccurred() in QAbstractSocket and its heirs
+ (QTcpSocket, QUdpSocket, QLocalSocket, and QSslSocket), and in QNetworkReply.
+ Code such as:
+
+ \code
+ connect(socket, qOverload<QAbstractSocket::SocketError>(&QAbstractSocket::error),
+ this, &SomeClass::errorSlot);
+ \endcode
+
+ must therefore be changed to:
+
+ \code
+ connect(socket, &QAbstractSocket::errorOccurred, this, &SomeClass::errorSlot);
+ \endcode
+
+ In QSslSocket, the function that returns a list of errors encountered
+ during the TLS handshake:
+
+ \code
+ QList<QSslError> sslErrors() const;
+ \endcode
+
+ is renamed to sslHandshakeErrors():
+
+ \code
+ const auto tlsErrors = socket.sslHandshakeErrors();
+ \endcode
+
+ \section2 Bearer management is removed
+
+ The classes QNetworkConfiguration and QNetworkConfigurationManager are removed in Qt 6.
+ Consequently, the following member functions of QNetworkAccessManager are also removed:
+
+ \code
+ void setConfiguration(const QNetworkConfiguration &config);
+ QNetworkConfiguration configuration() const;
+ QNetworkConfiguration activeConfiguration() const;
+ void setNetworkAccessible(NetworkAccessibility accessible);
+ NetworkAccessibility networkAccessible() const;
+ void networkSessionConnected();
+ \endcode
+
+ QNetworkInformation, initially introduced in Qt 6.1, aims to replace some
+ aspects of the bearer management API. It works by providing a unified API
+ which subscribes to changes to the network as notified by the operating
+ system.
+
+ QNetworkInformation::reachability(), introduced in Qt 6.1, replaces the
+ QNetworkAccessManager::networkAccessible() function, while adding more
+ detailed information about the reachability of the network. See its
+ documentation for more details.
+
+ In Qt 6.2 QNetworkInformation gained the ability to detect captive portals.
+
+ In Qt 6.3 QNetworkInformation gained QNetworkInformation::transportMedium()
+ and QNetworkInformation::isMetered().
+
+ \section2 Deleted enumerators
+
+ Several enumerators are removed in QtNetwork. This includes constants
+ for no longer supported protocols and functionality:
+
+ \list
+ \li QSsl::SslV2;
+ \li QSsl::SslV3;
+ \li QSsl::TlsV1SslV3;
+ \li QNetworkRequest::SpdyAllowedAttribute;
+ \li QNetworkRequest::SpdyWasUsedAttribute;
+ \li QNetworkAccessManager::UnknownAccessibility;
+ \li QNetworkAccessManager::NotAccessible;
+ \li QNetworkAccessManager::Accessible
+ \endlist
+
+ and enumerators whose names did not follow proper naming conventions:
+
+ \list
+ \li QSsl::TlsV1 (QSsl::TlsV1_0 is the proper name);
+ \li QNetworkRequest::HTTP2AllowedAttribute (use QNetworkRequest::Http2AllowedAttribute);
+ \li QNetworkRequest::HTTP2WasUsedAttribute (use QNetworkRequest::Http2WasUsedAttribute).
+ \endlist
+
+ QNetworkRequest::FollowRedirectsAttribute is removed in Qt 6, see
+ \l {Redirect policies}{the section about redirects handling} below.
+
+ \section2 Configuring QSslSocket
+
+ The following deprecated functions are removed in Qt 6:
+
+ \code
+ QList<QSslCipher> ciphers() const;
+ void setCiphers(const QList<QSslCipher> &ciphers);
+ void setCiphers(const QString &ciphers);
+ static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> supportedCiphers();
+ QList<QSslCipher> ciphers() const;
+ void setCiphers(const QList<QSslCipher> &ciphers);
+ void setCiphers(const QString &ciphers);
+ static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> supportedCiphers();
+ bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ void addCaCertificate(const QSslCertificate &certificate);
+ void addCaCertificates(const QList<QSslCertificate> &certificates);
+ void setCaCertificates(const QList<QSslCertificate> &certificates);
+ QList<QSslCertificate> caCertificates() const;
+ static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ static void addDefaultCaCertificate(const QSslCertificate &certificate);
+ static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+ static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+ static QList<QSslCertificate> defaultCaCertificates();
+ static QList<QSslCertificate> systemCaCertificates();
+ \endcode
+
+ Use QSslConfiguration and its member functions to set these parameters, e.g.:
+
+ \code
+ auto sslConfiguration = QSslConfiguration::defaultConfiguration();
+ sslConfiguration.setCiphers("ECDHE-ECDSA-AES256-SHA384");
+ // Set other parameters here ...
+ socket.setSslConfiguration(sslConfiguration);
+ \endcode
+
+ \section1 Changes in QNetworkAccessManager's default behavior
+
+ \section2 Redirect policies
+
+ In Qt 6, the default redirect policy has changed from manual to
+ QNetworkRequest::NoLessSafeRedirectPolicy. If your application relies
+ on manual redirect handling (it connects its slot to the QNetworkReply::redirected
+ signal), you have to explicitly set this policy when creating a request:
+
+ \code
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ \endcode
+
+ \section2 HTTP/2 is enabled by default
+
+ In Qt 6 QNetworkAccessManager enables HTTP/2 protocol by default. Depending on the
+ scheme ("https" or "http"), QNetworkAccessManager will use the Application Layer
+ Protocol Negotiation TLS extension or "protocol upgrade" HTTP header to negotiate HTTP/2.
+ If HTTP/2 cannot be negotiated, the access manager will fall back to using HTTP/1.1.
+ If your application can only use HTTP/1.1, you have to disable HTTP/2 manually
+ on a new request:
+
+ \code
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
+ \endcode
+
+ \section2 QNetworkAccessManager now guards against archive bombs
+
+ Starting with Qt 6.2 QNetworkAccessManager will guard against compressed
+ files that decompress to files which are much larger than their compressed
+ form by erroring out the reply if the decompression ratio exceeds a certain
+ threshold.
+ This check is only applied to files larger than a certain size, which can be
+ customized (or disabled by passing -1) by calling
+ \l{QNetworkRequest::setDecompressedSafetyCheckThreshold()}.
+*/
diff --git a/src/network/doc/src/qtnetwork.qdoc b/src/network/doc/src/qtnetwork.qdoc
index c931a1c19f..629c7113db 100644
--- a/src/network/doc/src/qtnetwork.qdoc
+++ b/src/network/doc/src/qtnetwork.qdoc
@@ -1,61 +1,36 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtnetwork-index.html
\title Qt Network
\brief Provides networking capabilities
- Qt Network provides a set of APIs for programming applications that use
- TCP/IP. Operations such as requests, cookies, and sending data over HTTP
- are handled by various C++ classes.
+ The Qt Network module provides a set of APIs for programming
+ applications that use TCP/IP. Operations such as requests, cookies, and
+ sending data over HTTP are handled by various C++ classes.
- \section1 Getting Started
+ \section1 Using the Module
- To use Qt Network classes,add this directive into the C++ files:
- \code
- #include <QtNetwork>
- \endcode
+ \include {module-use.qdocinc} {using the c++ api}
- \if !defined(qtforpython)
- To link against the Qt Network module, add this line to the project file:
- \code
- QT += network
- \endcode
- \endif
+ \section2 Building with CMake
+
+ \include {module-use.qdocinc} {building with cmake} {Network}
+
+ \section2 Building with qmake
+
+ \include {module-use.qdocinc} {building_with_qmake} {network}
\section1 Articles and Guides
These articles contain information about Qt Network setup and about
applications with networking capabilities.
\list
- \li \l{Network Programming with Qt} - Programming applications with networking capabilities
- \li \l{Bearer Management} - An API to control the system's connectivity state
- \li \l{Secure Sockets Layer (SSL) Classes} - Classes for secure communication over network sockets
+ \li \l{Network Programming with Qt} - Programming applications with
+ networking capabilities
+ \li \l{Secure Sockets Layer (SSL) Classes} - Classes for secure
+ communication over network sockets
\endlist
\section1 API Reference
@@ -64,6 +39,10 @@
\li \l{Qt Network C++ Classes}{C++ Classes}
\endlist
+ \section1 Module Evolution
+ \l{Changes to Qt Network} lists important changes in the module API
+ and functionality that were done for the Qt 6 series of Qt.
+
\section1 Licenses and Attributions
Qt Network is available under commercial licenses from \l{The Qt Company}.
@@ -72,7 +51,13 @@
the \l{GNU General Public License, version 2}.
See \l{Qt Licensing} for further details.
- Qt Network can use the \l{OpenSSL Toolkit} as a backend. The library is then
+ Furthermore, Qt Network in Qt \QtVersion may contain third-party
+ modules under the following permissive licenses:
+
+ \generatelist{groupsbymodule attributions-qtnetwork}
+
+ Qt Network can make use of the \l{OpenSSL Toolkit} as a back end.
+ The library is then
linked against OpenSSL in a way that requires compliance with the \l{OpenSSL
License}. To allow linking OpenSSL with Qt Network under the GPL, following
exceptions to the GPL do apply:
@@ -98,16 +83,11 @@
\module QtNetwork
\title Qt Network C++ Classes
\ingroup modules
+ \qtcmakepackage Network
\qtvariable network
\brief Provides classes to make network programming easier and portable.
- To include the definitions of the module's classes, use the
- following directive:
-
- \snippet code/doc_src_qtnetwork.cpp 1
- To link against the module, add this line to your \l qmake \c
- .pro file:
+ The \l{Qt Network} page contains information about how to use the module.
- \snippet code/doc_src_qtnetwork.pro 0
*/
diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc
index e485a1b393..83549f61e8 100644
--- a/src/network/doc/src/ssl.qdoc
+++ b/src/network/doc/src/ssl.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page ssl.html
@@ -33,39 +9,64 @@
\keyword SSL
The classes below provide support for secure network communication using
- the Secure Sockets Layer (SSL) protocol, using the \l{OpenSSL Toolkit}
- to perform encryption and protocol handling.
+ the Secure Sockets Layer (SSL) protocol, using a native TLS backend,
+ the \l{OpenSSL Toolkit}, or any appropriate TLS plugin to perform encryption
+ and protocol handling.
- From Qt version 5.6 onwards, the officially supported version for OpenSSL
- is 1.0.0 or later.
+ From Qt version 5.15 onward, the officially supported version for OpenSSL
+ is 1.1.1 or later.
+
+ Qt version 5.15.1 onward is also compatible with OpenSSL 3.
\annotatedlist ssl
+ For Android applications see \l{Adding OpenSSL Support for Android}.
+
+ \section1 Enabling and Disabling SSL Support when Building Qt from Source
- \section1 Enabling and Disabling SSL Support
+ When building Qt from source, Qt builds plugins for native TLS libraries
+ that are supported for the operating system you are building for. For
+ Windows this means
+ \l{https://docs.microsoft.com/en-us/windows/win32/com/schannel}{Schannel},
+ while for macOS this is
+ \l{https://developer.apple.com/documentation/security/secure_transport}{Secure Transport}.
- When building Qt from source, the configuration system checks for the presence
- of the \c{openssl/opensslv.h} header provided by source or developer packages
- of OpenSSL.
+ On all platforms, the configuration system checks for the presence of the
+ \c{openssl/opensslv.h} header provided by source or developer packages
+ of OpenSSL. If found, it will enable and build the OpenSSL backend for Qt.
- By default, an SSL-enabled Qt library dynamically loads any installed OpenSSL
- library at run-time. However, it is possible to link against the library at
- compile-time by configuring Qt with the \c{-openssl-linked} option.
+ By default, an OpenSSL-enabled Qt library dynamically loads any installed
+ OpenSSL library at run-time. However, it is possible to link against the
+ library at compile-time by configuring Qt with the \c{-openssl-linked}
+ option.
- When building a version of Qt linked against OpenSSL, the build system will
- attempt to link with libssl and libcrypt libraries located in the default
- location on the developer's system. This location is configurable:
- set the \c OPENSSL_LIBS environment variable to contain the linker options
- required to link Qt against the installed library. For example, on a Unix/Linux
- system:
+ When building a version of Qt linked against OpenSSL, Qt's build system will
+ use CMake's \c{FindOpenSSL} command to find OpenSSL in several standard
+ locations. You can set the CMake variable OPENSSL_ROOT_DIR to force a
+ specific location.
+ For example:
\code
- OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked
+ configure -openssl-linked -- -D OPENSSL_ROOT_DIR=<openssl_dir>
\endcode
To disable SSL support in a Qt build, configure Qt with the \c{-no-openssl}
option.
+ \section1 Considerations While Packaging Your Application
+
+ When you package your application, you may run a tool like \l{windeployqt}. This
+ copies all the plugins for the libraries you use to the \c{plugins/} folder.
+ However, for TLS you only need one backend, and you may delete the other
+ plugins before packaging your application. For example, if you're on Windows
+ and don't require any of the extra features the OpenSSL backend provides,
+ you can choose to forego shipping the \c{qopensslbackend} plugin as well as
+ the OpenSSL library, and simply ship the \c{qschannelbackend} plugin.
+
+ However, shipping multiple backends is not a problem. Qt will
+ attempt to load the backends in order (with OpenSSL attempted first) until
+ one is successfully loaded. The other backends are then unused.
+
\section1 Datagram Transport Layer Security
Datagram Transport Layer Security (DTLS) is a protocol that enables security
@@ -73,7 +74,7 @@
eavesdropping, tampering, or message forgery. The DTLS protocol is based on the
stream-oriented Transport Layer Security (TLS) protocol. QtNetwork enables
the use of DTLS with User Datagram Protocol (UDP), as defined by
- \l {https://tools.ietf.org/html/rfc6347}{RFC 6347}.
+ \l {RFC 6347}.
\section1 Import and Export Restrictions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
deleted file mode 100644
index 110d9f56bf..0000000000
--- a/src/network/kernel/kernel.pri
+++ /dev/null
@@ -1,95 +0,0 @@
-# Qt network kernel module
-
-PRECOMPILED_HEADER = ../corelib/global/qt_pch.h
-INCLUDEPATH += $$PWD
-
-HEADERS += kernel/qtnetworkglobal.h \
- kernel/qtnetworkglobal_p.h \
- kernel/qauthenticator.h \
- kernel/qauthenticator_p.h \
- kernel/qhostaddress.h \
- kernel/qhostaddress_p.h \
- kernel/qhostinfo.h \
- kernel/qhostinfo_p.h \
- kernel/qnetworkdatagram.h \
- kernel/qnetworkdatagram_p.h \
- kernel/qnetworkinterface.h \
- kernel/qnetworkinterface_p.h \
- kernel/qnetworkinterface_unix_p.h \
- kernel/qnetworkproxy.h \
- kernel/qnetconmonitor_p.h
-
-SOURCES += kernel/qauthenticator.cpp \
- kernel/qhostaddress.cpp \
- kernel/qhostinfo.cpp \
- kernel/qnetworkdatagram.cpp \
- kernel/qnetworkinterface.cpp \
- kernel/qnetworkproxy.cpp
-
-qtConfig(ftp) {
- HEADERS += kernel/qurlinfo_p.h
- SOURCES += kernel/qurlinfo.cpp
-}
-
-qtConfig(dnslookup) {
- HEADERS += kernel/qdnslookup.h \
- kernel/qdnslookup_p.h
-
- SOURCES += kernel/qdnslookup.cpp
-}
-
-unix {
- !integrity:qtConfig(dnslookup): SOURCES += kernel/qdnslookup_unix.cpp
-
- SOURCES += kernel/qhostinfo_unix.cpp
-
- qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl
-
- qtConfig(linux-netlink): SOURCES += kernel/qnetworkinterface_linux.cpp
- else: SOURCES += kernel/qnetworkinterface_unix.cpp
-}
-
-android:qtConfig(dnslookup) {
- SOURCES -= kernel/qdnslookup_unix.cpp
- SOURCES += kernel/qdnslookup_android.cpp
-}
-
-win32: {
- SOURCES += kernel/qhostinfo_win.cpp
-
- !winrt {
- SOURCES += kernel/qnetworkinterface_win.cpp
- qtConfig(dnslookup): SOURCES += kernel/qdnslookup_win.cpp
- LIBS_PRIVATE += -ldnsapi -liphlpapi
- } else {
- SOURCES += kernel/qnetworkinterface_winrt.cpp
- qtConfig(dnslookup): SOURCES += kernel/qdnslookup_winrt.cpp
- }
-}
-
-mac {
- LIBS_PRIVATE += -framework CoreFoundation
- !uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
-}
-
-macos | ios {
- OBJECTIVE_SOURCES += \
- kernel/qnetconmonitor_darwin.mm
-
- LIBS_PRIVATE += -framework SystemConfiguration
-} else:qtConfig(netlistmgr) {
- SOURCES += kernel/qnetconmonitor_win.cpp
-} else {
- SOURCES += kernel/qnetconmonitor_stub.cpp
-}
-
-qtConfig(gssapi): QMAKE_USE_PRIVATE += gssapi
-
-uikit:HEADERS += kernel/qnetworkinterface_uikit_p.h
-osx:SOURCES += kernel/qnetworkproxy_mac.cpp
-else:win32:!winrt: SOURCES += kernel/qnetworkproxy_win.cpp
-else: qtConfig(libproxy) {
- SOURCES += kernel/qnetworkproxy_libproxy.cpp
- QMAKE_USE_PRIVATE += libproxy libdl
-}
-else:SOURCES += kernel/qnetworkproxy_generic.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index e9a8e2a9e5..e42450d7e5 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qauthenticator.h>
#include <qauthenticator_p.h>
#include <qdebug.h>
+#include <qloggingcategory.h>
#include <qhash.h>
#include <qbytearray.h>
#include <qcryptographichash.h>
@@ -49,6 +14,7 @@
#include <qstring.h>
#include <qdatetime.h>
#include <qrandom.h>
+#include <QtNetwork/qhttpheaders.h>
#ifdef Q_OS_WIN
#include <qmutex.h>
@@ -68,17 +34,23 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_LOGGING_CATEGORY(lcAuthenticator);
+Q_LOGGING_CATEGORY(lcAuthenticator, "qt.network.authenticator");
+
static QByteArray qNtlmPhase1();
static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
#if QT_CONFIG(sspi) // SSPI
+static bool q_SSPI_library_load();
static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
- const QString& host);
+ QStringView host);
static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
- const QString& host, const QByteArray& challenge = QByteArray());
+ QStringView host, QByteArrayView challenge = {});
#elif QT_CONFIG(gssapi) // GSSAPI
-static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString& host);
-static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx,
- const QByteArray& challenge = QByteArray());
+static bool qGssapiTestGetCredentials(QStringView host);
+static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host);
+static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge = {});
#endif // gssapi
/*!
@@ -148,7 +120,28 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx,
\section2 SPNEGO/Negotiate
- This authentication mechanism currently supports no incoming or outgoing options.
+ \table
+ \header
+ \li Option
+ \li Direction
+ \li Type
+ \li Description
+ \row
+ \li \tt{spn}
+ \li Outgoing
+ \li QString
+ \li Provides a custom SPN.
+ \endtable
+
+ This authentication mechanism currently supports no incoming options.
+
+ The \c{spn} property is used on Windows clients when an SSPI library is used.
+ If the property is not set, a default SPN will be used. The default SPN on
+ Windows is \c {HTTP/<hostname>}.
+
+ Other operating systems use GSSAPI libraries. For that it is expected that
+ KDC is set up, and the credentials can be fetched from it. The backend always
+ uses \c {HTTPS@<hostname>} as an SPN.
\sa QSslSocket
*/
@@ -189,7 +182,7 @@ QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
if (d == other.d)
return *this;
- // Do not share the d since challange reponse/based changes
+ // Do not share the d since challenge response/based changes
// could corrupt the internal store and different network requests
// can utilize different types of proxies.
detach();
@@ -248,9 +241,11 @@ QString QAuthenticator::user() const
*/
void QAuthenticator::setUser(const QString &user)
{
- detach();
- d->user = user;
- d->updateCredentials();
+ if (!d || d->user != user) {
+ detach();
+ d->user = user;
+ d->updateCredentials();
+ }
}
/*!
@@ -268,8 +263,10 @@ QString QAuthenticator::password() const
*/
void QAuthenticator::setPassword(const QString &password)
{
- detach();
- d->password = password;
+ if (!d || d->password != password) {
+ detach();
+ d->password = password;
+ }
}
/*!
@@ -299,8 +296,10 @@ QString QAuthenticator::realm() const
*/
void QAuthenticator::setRealm(const QString &realm)
{
- detach();
- d->realm = realm;
+ if (!d || d->realm != realm) {
+ detach();
+ d->realm = realm;
+ }
}
/*!
@@ -340,8 +339,10 @@ QVariantHash QAuthenticator::options() const
*/
void QAuthenticator::setOption(const QString &opt, const QVariant &value)
{
- detach();
- d->options.insert(opt, value);
+ if (option(opt) != value) {
+ detach();
+ d->options.insert(opt, value);
+ }
}
@@ -392,7 +393,7 @@ void QAuthenticatorPrivate::updateCredentials()
switch (method) {
case QAuthenticatorPrivate::Ntlm:
- if ((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) {
+ if ((separatorPosn = user.indexOf("\\"_L1)) != -1) {
//domain name is present
realm.clear();
userDomain = user.left(separatorPosn);
@@ -409,9 +410,49 @@ void QAuthenticatorPrivate::updateCredentials()
}
}
-void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy)
+bool QAuthenticatorPrivate::isMethodSupported(QByteArrayView method)
{
- const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
+ Q_ASSERT(!method.startsWith(' ')); // This should be trimmed during parsing
+ auto separator = method.indexOf(' ');
+ if (separator != -1)
+ method = method.first(separator);
+ const auto isSupported = [method](QByteArrayView reference) {
+ return method.compare(reference, Qt::CaseInsensitive) == 0;
+ };
+ static const char methods[][10] = {
+ "basic",
+ "ntlm",
+ "digest",
+#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
+ "negotiate",
+#endif
+ };
+ return std::any_of(methods, methods + std::size(methods), isSupported);
+}
+
+static bool verifyDigestMD5(QByteArrayView value)
+{
+ auto opts = QAuthenticatorPrivate::parseDigestAuthenticationChallenge(value);
+ if (auto it = opts.constFind("algorithm"); it != opts.cend()) {
+ QByteArray alg = it.value();
+ if (alg.size() < 3)
+ return false;
+ // Just compare the first 3 characters, that way we match other subvariants as well, such as
+ // "MD5-sess"
+ auto view = QByteArrayView(alg).first(3);
+ return view.compare("MD5", Qt::CaseInsensitive) == 0;
+ }
+ return true; // assume it's ok if algorithm is not specified
+}
+
+void QAuthenticatorPrivate::parseHttpResponse(const QHttpHeaders &headers,
+ bool isProxy, QStringView host)
+{
+#if !QT_CONFIG(gssapi)
+ Q_UNUSED(host);
+#endif
+ const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
+ : QHttpHeaders::WellKnownHeader::WWWAuthenticate;
method = None;
/*
@@ -424,35 +465,56 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
authentication parameters.
*/
- QByteArray headerVal;
- for (int i = 0; i < values.size(); ++i) {
- const QPair<QByteArray, QByteArray> &current = values.at(i);
- if (current.first.compare(search, Qt::CaseInsensitive) != 0)
- continue;
- QByteArray str = current.second.toLower();
- if (method < Basic && str.startsWith("basic")) {
+ QByteArrayView headerVal;
+ for (const auto &current : headers.values(search)) {
+ const QLatin1StringView str(current);
+ if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
method = Basic;
- headerVal = current.second.mid(6);
- } else if (method < Ntlm && str.startsWith("ntlm")) {
+ headerVal = QByteArrayView(current).mid(6);
+ } else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
method = Ntlm;
- headerVal = current.second.mid(5);
- } else if (method < DigestMd5 && str.startsWith("digest")) {
+ headerVal = QByteArrayView(current).mid(5);
+ } else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
+ // Make sure the algorithm is actually MD5 before committing to it:
+ if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
+ continue;
+
method = DigestMd5;
- headerVal = current.second.mid(7);
- } else if (method < Negotiate && str.startsWith("negotiate")) {
+ headerVal = QByteArrayView(current).mid(7);
+ } else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
+#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
+#if QT_CONFIG(gssapi)
+ // For GSSAPI there needs to be a KDC set up for the host (afaict).
+ // So let's only conditionally use it if we can fetch the credentials.
+ // Sadly it's a bit slow because it requires a DNS lookup.
+ if (!qGssapiTestGetCredentials(host))
+ continue;
+#endif
method = Negotiate;
- headerVal = current.second.mid(10);
+ headerVal = QByteArrayView(current).mid(10);
+#endif
}
}
// Reparse credentials since we know the method now
updateCredentials();
- challenge = headerVal.trimmed();
+ challenge = headerVal.trimmed().toByteArray();
QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
+ // Sets phase to Start if this updates our realm and sets the two locations where we store
+ // realm
+ auto privSetRealm = [this](QString newRealm) {
+ if (newRealm != realm) {
+ if (phase == Done)
+ phase = Start;
+ realm = newRealm;
+ this->options["realm"_L1] = realm;
+ }
+ };
+
switch(method) {
case Basic:
- this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ privSetRealm(QString::fromLatin1(options.value("realm")));
if (user.isEmpty() && password.isEmpty())
phase = Done;
break;
@@ -461,9 +523,11 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
// work is done in calculateResponse()
break;
case DigestMd5: {
- this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
- if (options.value("stale").compare("true", Qt::CaseInsensitive) == 0)
+ privSetRealm(QString::fromLatin1(options.value("realm")));
+ if (options.value("stale").compare("true", Qt::CaseInsensitive) == 0) {
phase = Start;
+ nonceCount = 0;
+ }
if (user.isEmpty() && password.isEmpty())
phase = Done;
break;
@@ -475,22 +539,21 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
}
}
-QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path, const QString& host)
+QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod,
+ QByteArrayView path, QStringView host)
{
#if !QT_CONFIG(sspi) && !QT_CONFIG(gssapi)
Q_UNUSED(host);
#endif
QByteArray response;
- const char* methodString = nullptr;
+ QByteArrayView methodString;
switch(method) {
case QAuthenticatorPrivate::None:
- methodString = "";
phase = Done;
break;
case QAuthenticatorPrivate::Basic:
methodString = "Basic";
- response = user.toLatin1() + ':' + password.toLatin1();
- response = response.toBase64();
+ response = (user + ':'_L1 + password).toLatin1().toBase64();
phase = Done;
break;
case QAuthenticatorPrivate::DigestMd5:
@@ -503,8 +566,13 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
if (challenge.isEmpty()) {
#if QT_CONFIG(sspi) // SSPI
QByteArray phase1Token;
- if (user.isEmpty()) // Only pull from system if no user was specified in authenticator
+ if (user.isEmpty()) { // Only pull from system if no user was specified in authenticator
phase1Token = qSspiStartup(this, method, host);
+ } else if (!q_SSPI_library_load()) {
+ // Since we're not running qSspiStartup we have to make sure the library is loaded
+ qWarning("Failed to load the SSPI libraries");
+ return "";
+ }
if (!phase1Token.isEmpty()) {
response = phase1Token.toBase64();
phase = Phase2;
@@ -531,6 +599,7 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
phase = Done;
}
+ challenge = "";
}
break;
@@ -549,42 +618,59 @@ QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMet
phase = Phase2;
} else {
phase = Done;
+ return "";
}
} else {
QByteArray phase3Token;
#if QT_CONFIG(sspi) // SSPI
- phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
+ if (sspiWindowsHandles)
+ phase3Token = qSspiContinue(this, method, host, QByteArray::fromBase64(challenge));
#elif QT_CONFIG(gssapi) // GSSAPI
- phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
+ if (gssApiHandles)
+ phase3Token = qGssapiContinue(this, QByteArray::fromBase64(challenge));
#endif
if (!phase3Token.isEmpty()) {
response = phase3Token.toBase64();
phase = Done;
+ challenge = "";
+ } else {
+ phase = Done;
+ return "";
}
}
break;
}
- return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
+ return methodString + ' ' + response;
}
// ---------------------------- Digest Md5 code ----------------------------------------
-QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
+static bool containsAuth(QByteArrayView data)
+{
+ for (auto element : QLatin1StringView(data).tokenize(','_L1)) {
+ if (element == "auth"_L1)
+ return true;
+ }
+ return false;
+}
+
+QHash<QByteArray, QByteArray>
+QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challenge)
{
QHash<QByteArray, QByteArray> options;
// parse the challenge
- const char *d = challenge.constData();
- const char *end = d + challenge.length();
+ const char *d = challenge.data();
+ const char *end = d + challenge.size();
while (d < end) {
while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
++d;
const char *start = d;
while (d < end && *d != '=')
++d;
- QByteArray key = QByteArray(start, d - start);
+ QByteArrayView key = QByteArrayView(start, d - start);
++d;
if (d >= end)
break;
@@ -593,7 +679,6 @@ QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationCh
++d;
if (d >= end)
break;
- start = d;
QByteArray value;
while (d < end) {
bool backslash = false;
@@ -616,13 +701,12 @@ QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationCh
while (d < end && *d != ',')
++d;
++d;
- options[key] = value;
+ options[key.toByteArray()] = std::move(value);
}
QByteArray qop = options.value("qop");
if (!qop.isEmpty()) {
- QList<QByteArray> qopoptions = qop.split(',');
- if (!qopoptions.contains("auth"))
+ if (!containsAuth(qop))
return QHash<QByteArray, QByteArray>();
// #### can't do auth-int currently
// if (qop.contains("auth-int"))
@@ -648,24 +732,24 @@ QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationCh
/* calculate request-digest/response-digest as per HTTP Digest spec */
static QByteArray digestMd5ResponseHelper(
- const QByteArray &alg,
- const QByteArray &userName,
- const QByteArray &realm,
- const QByteArray &password,
- const QByteArray &nonce, /* nonce from server */
- const QByteArray &nonceCount, /* 8 hex digits */
- const QByteArray &cNonce, /* client nonce */
- const QByteArray &qop, /* qop-value: "", "auth", "auth-int" */
- const QByteArray &method, /* method from the request */
- const QByteArray &digestUri, /* requested URL */
- const QByteArray &hEntity /* H(entity body) if qop="auth-int" */
+ QByteArrayView alg,
+ QByteArrayView userName,
+ QByteArrayView realm,
+ QByteArrayView password,
+ QByteArrayView nonce, /* nonce from server */
+ QByteArrayView nonceCount, /* 8 hex digits */
+ QByteArrayView cNonce, /* client nonce */
+ QByteArrayView qop, /* qop-value: "", "auth", "auth-int" */
+ QByteArrayView method, /* method from the request */
+ QByteArrayView digestUri, /* requested URL */
+ QByteArrayView hEntity /* H(entity body) if qop="auth-int" */
)
{
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(userName);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(realm);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(password);
QByteArray ha1 = hash.result();
if (alg.compare("md5-sess", Qt::CaseInsensitive) == 0) {
@@ -675,9 +759,9 @@ static QByteArray digestMd5ResponseHelper(
// but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
// must be the following line:
hash.addData(ha1.toHex());
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(nonce);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(cNonce);
ha1 = hash.result();
};
@@ -686,10 +770,10 @@ static QByteArray digestMd5ResponseHelper(
// calculate H(A2)
hash.reset();
hash.addData(method);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(digestUri);
if (qop.compare("auth-int", Qt::CaseInsensitive) == 0) {
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(hEntity);
}
QByteArray ha2hex = hash.result().toHex();
@@ -697,28 +781,29 @@ static QByteArray digestMd5ResponseHelper(
// calculate response
hash.reset();
hash.addData(ha1);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(nonce);
- hash.addData(":", 1);
+ hash.addData(":");
if (!qop.isNull()) {
hash.addData(nonceCount);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(cNonce);
- hash.addData(":", 1);
+ hash.addData(":");
hash.addData(qop);
- hash.addData(":", 1);
+ hash.addData(":");
}
hash.addData(ha2hex);
return hash.result().toHex();
}
-QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path)
+QByteArray QAuthenticatorPrivate::digestMd5Response(QByteArrayView challenge, QByteArrayView method,
+ QByteArrayView path)
{
QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
++nonceCount;
QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
- while (nonceCountString.length() < 8)
+ while (nonceCountString.size() < 8)
nonceCountString.prepend('0');
QByteArray nonce = options.value("nonce");
@@ -979,9 +1064,9 @@ static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
qStreamNtlmBuffer(ds, s.toLatin1());
return;
}
- const ushort *d = s.utf16();
- for (int i = 0; i < s.length(); ++i)
- ds << d[i];
+
+ for (QChar ch : s)
+ ds << quint16(ch.unicode());
}
@@ -999,7 +1084,7 @@ static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, boo
{
if (!unicode)
return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
- buf.len = 2 * s.length();
+ buf.len = 2 * s.size();
buf.maxLen = buf.len;
buf.offset = (offset + 1) & ~1;
return buf.offset + buf.len;
@@ -1121,12 +1206,11 @@ static QByteArray qNtlmPhase1()
static QByteArray qStringAsUcs2Le(const QString& src)
{
- QByteArray rc(2*src.length(), 0);
- const unsigned short *s = src.utf16();
+ QByteArray rc(2*src.size(), 0);
unsigned short *d = (unsigned short*)rc.data();
- for (int i = 0; i < src.length(); ++i) {
- d[i] = qToLittleEndian(s[i]);
- }
+ for (QChar ch : src)
+ *d++ = qToLittleEndian(quint16(ch.unicode()));
+
return rc;
}
@@ -1135,7 +1219,7 @@ static QString qStringFromUcs2Le(QByteArray src)
{
Q_ASSERT(src.size() % 2 == 0);
unsigned short *d = (unsigned short*)src.data();
- for (int i = 0; i < src.length() / 2; ++i) {
+ for (int i = 0; i < src.size() / 2; ++i) {
d[i] = qFromLittleEndian(d[i]);
}
return QString((const QChar *)src.data(), src.size()/2);
@@ -1164,13 +1248,12 @@ static QString qStringFromUcs2Le(QByteArray src)
* ---------------------------------------
*
*********************************************************************/
-QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
+QByteArray qEncodeHmacMd5(QByteArray &key, QByteArrayView message)
{
Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check");
Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check");
QCryptographicHash hash(QCryptographicHash::Md5);
- QByteArray hMsg;
QByteArray iKeyPad(blockSize, 0x36);
QByteArray oKeyPad(blockSize, 0x5c);
@@ -1178,7 +1261,7 @@ QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
hash.reset();
// Adjust the key length to blockSize
- if(blockSize < key.length()) {
+ if (blockSize < key.size()) {
hash.addData(key);
key = hash.result(); //MD5 will always return 16 bytes length output
}
@@ -1203,7 +1286,7 @@ QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
hash.reset();
hash.addData(iKeyPad);
- hMsg = hash.result();
+ QByteArrayView hMsg = hash.resultView();
//Digest gen after pass-1: H((K0 xor ipad)||text)
QByteArray hmacDigest;
@@ -1230,10 +1313,10 @@ static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx,
Q_ASSERT(phase3 != nullptr);
// since v2 Hash is need for both NTLMv2 and LMv2 it is calculated
// only once and stored and reused
- if(phase3->v2Hash.size() == 0) {
+ if (phase3->v2Hash.size() == 0) {
QCryptographicHash md4(QCryptographicHash::Md4);
QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
- md4.addData(passUnicode.data(), passUnicode.size());
+ md4.addData(passUnicode);
QByteArray hashKey = md4.result();
Q_ASSERT(hashKey.size() == 16);
@@ -1267,7 +1350,7 @@ static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff)
ds >> avId;
ds >> avLen;
while(avId != 0) {
- if(avId == AVTIMESTAMP) {
+ if (avId == AVTIMESTAMP) {
timeArray.resize(avLen);
//avLen size of QByteArray is allocated
ds.readRawData(timeArray.data(), avLen);
@@ -1302,13 +1385,13 @@ static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx,
quint64 time = 0;
QByteArray timeArray;
- if(ch.targetInfo.len)
+ if (ch.targetInfo.len)
{
timeArray = qExtractServerTime(ch.targetInfoBuff);
}
//if server sends time, use it instead of current time
- if(timeArray.size()) {
+ if (timeArray.size()) {
ds.writeRawData(timeArray.constData(), timeArray.size());
} else {
// number of seconds between 1601 and the epoch (1970)
@@ -1392,7 +1475,7 @@ static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch)
ds >> ch.targetInfo;
if (ch.targetName.len > 0) {
- if (ch.targetName.len + ch.targetName.offset > (unsigned)data.size())
+ if (qsizetype(ch.targetName.len + ch.targetName.offset) > data.size())
return false;
ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
@@ -1440,7 +1523,7 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase));
// for kerberos style user@domain logins, NTLM domain string should be left empty
- if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(QLatin1Char('@'))) {
+ if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(u'@')) {
offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
pb.domainStr = ch.targetNameStr;
} else {
@@ -1480,27 +1563,16 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
// See http://davenport.sourceforge.net/ntlm.html
// and libcurl http_ntlm.c
-// Handle of secur32.dll
-static HMODULE securityDLLHandle = nullptr;
// Pointer to SSPI dispatch table
-static PSecurityFunctionTable pSecurityFunctionTable = nullptr;
+static PSecurityFunctionTableW pSecurityFunctionTable = nullptr;
static bool q_SSPI_library_load()
{
- static QBasicMutex mutex;
+ Q_CONSTINIT static QBasicMutex mutex;
QMutexLocker l(&mutex);
- // Initialize security interface
- if (pSecurityFunctionTable == nullptr) {
- securityDLLHandle = LoadLibrary(L"secur32.dll");
- if (securityDLLHandle != nullptr) {
- INIT_SECURITY_INTERFACE pInitSecurityInterface =
- reinterpret_cast<INIT_SECURITY_INTERFACE>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(securityDLLHandle, "InitSecurityInterfaceW")));
- if (pInitSecurityInterface != nullptr)
- pSecurityFunctionTable = pInitSecurityInterface();
- }
- }
+ if (pSecurityFunctionTable == nullptr)
+ pSecurityFunctionTable = InitSecurityInterfaceW();
if (pSecurityFunctionTable == nullptr)
return false;
@@ -1509,7 +1581,7 @@ static bool q_SSPI_library_load()
}
static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
- const QString& host)
+ QStringView host)
{
if (!q_SSPI_library_load())
return QByteArray();
@@ -1518,14 +1590,28 @@ static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate
if (!ctx->sspiWindowsHandles)
ctx->sspiWindowsHandles.reset(new QSSPIWindowsHandles);
- memset(&ctx->sspiWindowsHandles->credHandle, 0, sizeof(CredHandle));
+ SecInvalidateHandle(&ctx->sspiWindowsHandles->credHandle);
+ SecInvalidateHandle(&ctx->sspiWindowsHandles->ctxHandle);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+ bool useAuth = false;
+ if (method == QAuthenticatorPrivate::Negotiate && !ctx->user.isEmpty()) {
+ auth.Domain = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->userDomain.constData()));
+ auth.DomainLength = ctx->userDomain.size();
+ auth.User = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->user.constData()));
+ auth.UserLength = ctx->user.size();
+ auth.Password = const_cast<ushort *>(reinterpret_cast<const ushort *>(ctx->password.constData()));
+ auth.PasswordLength = ctx->password.size();
+ useAuth = true;
+ }
// Acquire our credentials handle
SECURITY_STATUS secStatus = pSecurityFunctionTable->AcquireCredentialsHandle(
- nullptr,
- (SEC_WCHAR*)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
- SECPKG_CRED_OUTBOUND, nullptr, nullptr, nullptr, nullptr,
- &ctx->sspiWindowsHandles->credHandle, &expiry
+ nullptr,
+ (SEC_WCHAR *)(method == QAuthenticatorPrivate::Negotiate ? L"Negotiate" : L"NTLM"),
+ SECPKG_CRED_OUTBOUND, nullptr, useAuth ? &auth : nullptr, nullptr, nullptr,
+ &ctx->sspiWindowsHandles->credHandle, &expiry
);
if (secStatus != SEC_E_OK) {
ctx->sspiWindowsHandles.reset(nullptr);
@@ -1536,7 +1622,7 @@ static QByteArray qSspiStartup(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate
}
static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivate::Method method,
- const QString &host, const QByteArray &challenge)
+ QStringView host, QByteArrayView challenge)
{
QByteArray result;
SecBuffer challengeBuf;
@@ -1566,8 +1652,11 @@ static QByteArray qSspiContinue(QAuthenticatorPrivate *ctx, QAuthenticatorPrivat
responseBuf.cbBuffer = 0;
// Calculate target (SPN for Negotiate, empty for NTLM)
- std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
- ? QLatin1String("HTTP/") + host : QString()).toStdWString();
+ QString targetName = ctx->options.value("spn"_L1).toString();
+ if (targetName.isEmpty())
+ targetName = "HTTP/"_L1 + host;
+ const std::wstring targetNameW = (method == QAuthenticatorPrivate::Negotiate
+ ? targetName : QString()).toStdWString();
// Generate our challenge-response message
SECURITY_STATUS secStatus = pSecurityFunctionTable->InitializeSecurityContext(
@@ -1614,7 +1703,7 @@ static void q_GSSAPI_error_int(const char *message, OM_uint32 stat, int type)
do {
gss_display_status(&minStat, stat, type, GSS_C_NO_OID, &msgCtx, &msg);
- qDebug() << message << ": " << reinterpret_cast<const char*>(msg.value);
+ qCDebug(lcAuthenticator) << message << ": " << reinterpret_cast<const char*>(msg.value);
gss_release_buffer(&minStat, &msg);
} while (msgCtx);
}
@@ -1629,26 +1718,36 @@ static void q_GSSAPI_error(const char *message, OM_uint32 majStat, OM_uint32 min
q_GSSAPI_error_int(message, minStat, GSS_C_MECH_CODE);
}
-// Send initial GSS authentication token
-static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host)
+static gss_name_t qGSsapiGetServiceName(QStringView host)
{
- OM_uint32 majStat, minStat;
-
- if (!ctx->gssApiHandles)
- ctx->gssApiHandles.reset(new QGssApiHandles);
-
- // Convert target name to internal form
- QByteArray serviceName = QStringLiteral("HTTPS@%1").arg(host).toLocal8Bit();
+ QByteArray serviceName = "HTTPS@" + host.toLocal8Bit();
gss_buffer_desc nameDesc = {static_cast<std::size_t>(serviceName.size()), serviceName.data()};
- majStat = gss_import_name(&minStat, &nameDesc,
- GSS_C_NT_HOSTBASED_SERVICE, &ctx->gssApiHandles->targetName);
+ gss_name_t importedName;
+ OM_uint32 minStat;
+ OM_uint32 majStat = gss_import_name(&minStat, &nameDesc,
+ GSS_C_NT_HOSTBASED_SERVICE, &importedName);
if (majStat != GSS_S_COMPLETE) {
q_GSSAPI_error("gss_import_name error", majStat, minStat);
+ return nullptr;
+ }
+ return importedName;
+}
+
+// Send initial GSS authentication token
+static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, QStringView host)
+{
+ if (!ctx->gssApiHandles)
+ ctx->gssApiHandles.reset(new QGssApiHandles);
+
+ // Convert target name to internal form
+ gss_name_t name = qGSsapiGetServiceName(host);
+ if (name == nullptr) {
ctx->gssApiHandles.reset(nullptr);
return QByteArray();
}
+ ctx->gssApiHandles->targetName = name;
// Call qGssapiContinue with GSS_C_NO_CONTEXT to get initial packet
ctx->gssApiHandles->gssCtx = GSS_C_NO_CONTEXT;
@@ -1656,7 +1755,7 @@ static QByteArray qGssapiStartup(QAuthenticatorPrivate *ctx, const QString &host
}
// Continue GSS authentication with next token as needed
-static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray& challenge)
+static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView challenge)
{
OM_uint32 majStat, minStat, ignored;
QByteArray result;
@@ -1665,7 +1764,7 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray&
if (!challenge.isEmpty()) {
inBuf.value = const_cast<char*>(challenge.data());
- inBuf.length = challenge.length();
+ inBuf.length = challenge.size();
}
majStat = gss_init_sec_context(&minStat,
@@ -1702,6 +1801,28 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, const QByteArray&
return result;
}
+static bool qGssapiTestGetCredentials(QStringView host)
+{
+ gss_name_t serviceName = qGSsapiGetServiceName(host);
+ if (!serviceName)
+ return false; // Something was wrong with the service name, so skip this
+ OM_uint32 minStat;
+ gss_cred_id_t cred;
+ OM_uint32 majStat = gss_acquire_cred(&minStat, serviceName, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred, nullptr,
+ nullptr);
+
+ OM_uint32 ignored;
+ gss_release_name(&ignored, &serviceName);
+ gss_release_cred(&ignored, &cred);
+
+ if (majStat != GSS_S_COMPLETE) {
+ q_GSSAPI_error("gss_acquire_cred", majStat, minStat);
+ return false;
+ }
+ return true;
+}
+
// ---------------------------- End of GSSAPI code ----------------------------------------------
#endif // gssapi
diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h
index 1032c2f501..a05d359e93 100644
--- a/src/network/kernel/qauthenticator.h
+++ b/src/network/kernel/qauthenticator.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUTHENTICATOR_H
#define QAUTHENTICATOR_H
@@ -52,6 +16,7 @@ class QUrl;
class Q_NETWORK_EXPORT QAuthenticator
{
+ Q_GADGET
public:
QAuthenticator();
~QAuthenticator();
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index e201d22650..bc16139941 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUTHENTICATOR_P_H
#define QAUTHENTICATOR_P_H
@@ -62,16 +26,17 @@
QT_BEGIN_NAMESPACE
class QHttpResponseHeader;
+class QHttpHeaders;
#if QT_CONFIG(sspi) // SSPI
class QSSPIWindowsHandles;
#elif QT_CONFIG(gssapi) // GSSAPI
class QGssApiHandles;
#endif
-class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
+class Q_NETWORK_EXPORT QAuthenticatorPrivate
{
public:
- enum Method { None, Basic, Ntlm, DigestMd5, Negotiate };
+ enum Method { None, Basic, Negotiate, Ntlm, DigestMd5, };
QAuthenticatorPrivate();
~QAuthenticatorPrivate();
@@ -91,6 +56,7 @@ public:
enum Phase {
Start,
+ Phase1,
Phase2,
Done,
Invalid
@@ -105,16 +71,20 @@ public:
QString workstation;
QString userDomain;
- QByteArray calculateResponse(const QByteArray &method, const QByteArray &path, const QString& host);
+ QByteArray calculateResponse(QByteArrayView method, QByteArrayView path, QStringView host);
inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
- QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path);
- static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge);
+ QByteArray digestMd5Response(QByteArrayView challenge, QByteArrayView method,
+ QByteArrayView path);
+ static QHash<QByteArray, QByteArray>
+ parseDigestAuthenticationChallenge(QByteArrayView challenge);
- void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy);
+ void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host);
void updateCredentials();
+
+ static bool isMethodSupported(QByteArrayView method);
};
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index ab1be02b6b..1b4db7130b 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -1,57 +1,43 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup.h"
#include "qdnslookup_p.h"
+#include <qapplicationstatic.h>
#include <qcoreapplication.h>
#include <qdatetime.h>
+#include <qendian.h>
+#include <qloggingcategory.h>
#include <qrandom.h>
+#include <qspan.h>
#include <qurl.h>
+#if QT_CONFIG(ssl)
+# include <qsslsocket.h>
+#endif
+
#include <algorithm>
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(thread)
-Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
-#endif
+using namespace Qt::StringLiterals;
+
+static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
+
+namespace {
+struct QDnsLookupThreadPool : QThreadPool
+{
+ QDnsLookupThreadPool()
+ {
+ // Run up to 5 lookups in parallel.
+ setMaxThreadCount(5);
+ }
+};
+}
+
+Q_APPLICATION_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
{
@@ -155,9 +141,6 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
}
}
-const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
- QT_TRANSLATE_NOOP("QDnsLookupRunnable", "IPv6 addresses for nameservers are currently not supported");
-
/*!
\class QDnsLookup
\brief The QDnsLookup class represents a DNS lookup.
@@ -184,6 +167,42 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\note If you simply want to find the IP address(es) associated with a host
name, or the host name associated with an IP address you should use
QHostInfo instead.
+
+ \section1 DNS-over-TLS and Authentic Data
+
+ QDnsLookup supports DNS-over-TLS (DoT, as specified by \l{RFC 7858}) on
+ some platforms. That currently includes all Unix platforms where regular
+ queries are supported, if \l QSslSocket support is present in Qt. To query
+ if support is present at runtime, use isProtocolSupported().
+
+ When using DNS-over-TLS, QDnsLookup only implements the "Opportunistic
+ Privacy Profile" method of authentication, as described in \l{RFC 7858}
+ section 4.1. In this mode, QDnsLookup (through \l QSslSocket) only
+ validates that the server presents a certificate that is valid for the
+ server being connected to. Clients may use setSslConfiguration() to impose
+ additional restrictions and sslConfiguration() to obtain information after
+ the query is complete.
+
+ QDnsLookup will request DNS servers queried over TLS to perform
+ authentication on the data they return. If they confirm the data is valid,
+ the \l authenticData property will be set to true. QDnsLookup does not
+ verify the integrity of the data by itself, so applications should only
+ trust this property on servers they have confirmed through other means to
+ be trustworthy.
+
+ \section2 Authentic Data without TLS
+
+ QDnsLookup request Authentic Data for any server set with setNameserver(),
+ even if TLS encryption is not required. This is useful when querying a
+ caching nameserver on the same host as the application or on a trusted
+ network. Though similar to the TLS case, the application is responsible for
+ determining if the server it chose to use is trustworthy, and if the
+ unencrypted connection cannot be tampered with.
+
+ QDnsLookup obeys the system configuration to request Authentic Data on the
+ default nameserver (that is, if setNameserver() is not called). This is
+ currently only supported on Linux systems using glibc 2.31 or later. On any
+ other systems, QDnsLookup will ignore the AD bit in the query header.
*/
/*!
@@ -212,6 +231,9 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\value NotFoundError the requested domain name does not exist
(NXDOMAIN).
+
+ \value TimeoutError the server was not reached or did not reply
+ in time (since 6.6).
*/
/*!
@@ -235,10 +257,72 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\value SRV service records.
+ \value[since 6.8] TLSA TLS association records.
+
\value TXT text records.
*/
/*!
+ \enum QDnsLookup::Protocol
+
+ Indicates the type of DNS server that is being queried.
+
+ \value Standard
+ Regular, unencrypted DNS, using UDP and falling back to TCP as necessary
+ (default port: 53)
+
+ \value DnsOverTls
+ Encrypted DNS over TLS (DoT, as specified by \l{RFC 7858}), over TCP
+ (default port: 853)
+
+ \sa isProtocolSupported(), nameserverProtocol, setNameserver()
+*/
+
+/*!
+ \since 6.8
+
+ Returns true if DNS queries using \a protocol are supported with QDnsLookup.
+
+ \sa nameserverProtocol
+*/
+bool QDnsLookup::isProtocolSupported(Protocol protocol)
+{
+#if QT_CONFIG(libresolv) || defined(Q_OS_WIN)
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return true;
+ case QDnsLookup::DnsOverTls:
+# if QT_CONFIG(ssl)
+ if (QSslSocket::supportsSsl())
+ return true;
+# endif
+ return false;
+ }
+#else
+ Q_UNUSED(protocol)
+#endif
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Returns the standard (default) port number for the protocol \a protocol.
+
+ \sa isProtocolSupported()
+*/
+quint16 QDnsLookup::defaultPortForProtocol(Protocol protocol) noexcept
+{
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return DnsPort;
+ case QDnsLookup::DnsOverTls:
+ return DnsOverTlsPort;
+ }
+ return 0; // will probably fail somewhere
+}
+
+/*!
\fn void QDnsLookup::finished()
This signal is emitted when the reply has finished processing.
@@ -267,8 +351,8 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
QDnsLookup::QDnsLookup(QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
- qRegisterMetaType<QDnsLookupReply>();
}
+
/*!
Constructs a QDnsLookup object for the given \a type and \a name and sets
\a parent as the parent object.
@@ -278,7 +362,6 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
}
@@ -286,21 +369,68 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
/*!
\fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
\since 5.4
- Constructs a QDnsLookup object for the given \a type, \a name and
- \a nameserver and sets \a parent as the parent object.
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on the default DNS port,
+ and sets \a parent as the parent object.
*/
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
+ : QDnsLookup(type, name, nameserver, 0, parent)
+{
+}
+
+/*!
+ \fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
+ \since 6.6
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+//! [nameserver-port]
+ \note Setting the port number to any value other than the default (53) can
+ cause the name resolution to fail, depending on the operating system
+ limitations and firewalls, if the nameserverProtocol() to be used
+ QDnsLookup::Standard. Notably, the Windows API used by QDnsLookup is unable
+ to handle alternate port numbers.
+//! [nameserver-port]
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
+ d->port = port;
d->nameserver = nameserver;
}
/*!
+ \since 6.8
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+ The query will be sent using \a protocol, if supported. Use
+ isProtocolSupported() to check if it is supported.
+
+ \include qdnslookup.cpp nameserver-port
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, Protocol protocol,
+ const QHostAddress &nameserver, quint16 port, QObject *parent)
+ : QObject(*new QDnsLookupPrivate, parent)
+{
+ Q_D(QDnsLookup);
+ d->name = name;
+ d->type = type;
+ d->nameserver = nameserver;
+ d->port = port;
+ d->protocol = protocol;
+}
+
+/*!
Destroys the QDnsLookup object.
It is safe to delete a QDnsLookup object even if it is not finished, you
@@ -312,6 +442,28 @@ QDnsLookup::~QDnsLookup()
}
/*!
+ \since 6.8
+ \property QDnsLookup::authenticData
+ \brief whether the reply was authenticated by the resolver.
+
+ QDnsLookup does not perform the authentication itself. Instead, it trusts
+ the name server that was queried to perform the authentication and report
+ it. The application is responsible for determining if any servers it
+ configured with setNameserver() are trustworthy; if no server was set,
+ QDnsLookup obeys system configuration on whether responses should be
+ trusted.
+
+ This property may be set even if error() indicates a resolver error
+ occurred.
+
+ \sa setNameserver(), nameserverProtocol()
+*/
+bool QDnsLookup::isAuthenticData() const
+{
+ return d_func()->reply.authenticData;
+}
+
+/*!
\property QDnsLookup::error
\brief the type of error that occurred if the DNS lookup failed, or NoError.
*/
@@ -344,6 +496,10 @@ bool QDnsLookup::isFinished() const
\property QDnsLookup::name
\brief the name to lookup.
+ If the name to look up is empty, QDnsLookup will attempt to resolve the
+ root domain of DNS. That query is usually performed with QDnsLookup::type
+ set to \l{QDnsLookup::Type}{NS}.
+
\note The name will be encoded using IDNA, which means it's unsuitable for
querying SRV records compatible with the DNS-SD specification.
*/
@@ -356,10 +512,13 @@ QString QDnsLookup::name() const
void QDnsLookup::setName(const QString &name)
{
Q_D(QDnsLookup);
- if (name != d->name) {
- d->name = name;
- emit nameChanged(name);
- }
+ d->name = name;
+}
+
+QBindable<QString> QDnsLookup::bindableName()
+{
+ Q_D(QDnsLookup);
+ return &d->name;
}
/*!
@@ -375,10 +534,13 @@ QDnsLookup::Type QDnsLookup::type() const
void QDnsLookup::setType(Type type)
{
Q_D(QDnsLookup);
- if (type != d->type) {
- d->type = type;
- emit typeChanged(type);
- }
+ d->type = type;
+}
+
+QBindable<QDnsLookup::Type> QDnsLookup::bindableType()
+{
+ Q_D(QDnsLookup);
+ return &d->type;
}
/*!
@@ -394,10 +556,83 @@ QHostAddress QDnsLookup::nameserver() const
void QDnsLookup::setNameserver(const QHostAddress &nameserver)
{
Q_D(QDnsLookup);
- if (nameserver != d->nameserver) {
- d->nameserver = nameserver;
- emit nameserverChanged(nameserver);
- }
+ d->nameserver = nameserver;
+}
+
+QBindable<QHostAddress> QDnsLookup::bindableNameserver()
+{
+ Q_D(QDnsLookup);
+ return &d->nameserver;
+}
+
+/*!
+ \property QDnsLookup::nameserverPort
+ \since 6.6
+ \brief the port number of nameserver to use for DNS lookup.
+
+ The value of 0 indicates that QDnsLookup should use the default port for
+ the nameserverProtocol().
+
+ \include qdnslookup.cpp nameserver-port
+*/
+
+quint16 QDnsLookup::nameserverPort() const
+{
+ return d_func()->port;
+}
+
+void QDnsLookup::setNameserverPort(quint16 nameserverPort)
+{
+ Q_D(QDnsLookup);
+ d->port = nameserverPort;
+}
+
+QBindable<quint16> QDnsLookup::bindableNameserverPort()
+{
+ Q_D(QDnsLookup);
+ return &d->port;
+}
+
+/*!
+ \property QDnsLookup::nameserverProtocol
+ \since 6.8
+ \brief the protocol to use when sending the DNS query
+
+ \sa isProtocolSupported()
+*/
+QDnsLookup::Protocol QDnsLookup::nameserverProtocol() const
+{
+ return d_func()->protocol;
+}
+
+void QDnsLookup::setNameserverProtocol(Protocol protocol)
+{
+ d_func()->protocol = protocol;
+}
+
+QBindable<QDnsLookup::Protocol> QDnsLookup::bindableNameserverProtocol()
+{
+ return &d_func()->protocol;
+}
+
+/*!
+ \fn void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+ \since 6.6
+
+ Sets the nameserver to \a nameserver and the port to \a port.
+
+ \include qdnslookup.cpp nameserver-port
+
+ \sa QDnsLookup::nameserver, QDnsLookup::nameserverPort
+*/
+
+void QDnsLookup::setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port)
+{
+ Qt::beginPropertyUpdateGroup();
+ setNameserver(nameserver);
+ setNameserverPort(port);
+ setNameserverProtocol(protocol);
+ Qt::endPropertyUpdateGroup();
}
/*!
@@ -472,6 +707,46 @@ QList<QDnsTextRecord> QDnsLookup::textRecords() const
}
/*!
+ \since 6.8
+ Returns the list of TLS association records associated with this lookup.
+
+ According to the standards relating to DNS-based Authentication of Named
+ Entities (DANE), this field should be ignored and must not be used for
+ verifying the authentity of a given server if the authenticity of the DNS
+ reply cannot itself be confirmed. See isAuthenticData() for more
+ information.
+ */
+QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords() const
+{
+ return d_func()->reply.tlsAssociationRecords;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ \since 6.8
+ Sets the \a sslConfiguration to use for outgoing DNS-over-TLS connections.
+
+ \sa sslConfiguration(), QSslSocket::setSslConfiguration()
+*/
+void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
+{
+ Q_D(QDnsLookup);
+ d->sslConfiguration.emplace(sslConfiguration);
+}
+
+/*!
+ Returns the current SSL configuration.
+
+ \sa setSslConfiguration()
+*/
+QSslConfiguration QDnsLookup::sslConfiguration() const
+{
+ const Q_D(QDnsLookup);
+ return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
+}
+#endif
+
+/*!
Aborts the DNS lookup operation.
If the lookup is already finished, does nothing.
@@ -501,13 +776,32 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup);
d->isFinished = false;
d->reply = QDnsLookupReply();
- d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
- connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
- this, SLOT(_q_lookupFinished(QDnsLookupReply)),
- Qt::BlockingQueuedConnection);
-#if QT_CONFIG(thread)
- theDnsLookupThreadPool()->start(d->runnable);
+ if (!QCoreApplication::instance()) {
+ // NOT qCWarning because this isn't a result of the lookup
+ qWarning("QDnsLookup requires a QCoreApplication");
+ return;
+ }
+
+ auto l = [this](const QDnsLookupReply &reply) {
+ Q_D(QDnsLookup);
+ if (d->runnable == sender()) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
#endif
+#if QT_CONFIG(ssl)
+ d->sslConfiguration = std::move(reply.sslConfiguration);
+#endif
+ d->reply = reply;
+ d->runnable = nullptr;
+ d->isFinished = true;
+ emit finished();
+ }
+ };
+
+ d->runnable = new QDnsLookupRunnable(d);
+ connect(d->runnable, &QDnsLookupRunnable::finished, this, l,
+ Qt::BlockingQueuedConnection);
+ theDnsLookupThreadPool->start(d->runnable);
}
/*!
@@ -984,18 +1278,249 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
very fast and never fails.
*/
-void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
+/*!
+ \class QDnsTlsAssociationRecord
+ \since 6.8
+ \brief The QDnsTlsAssociationRecord class stores information about a DNS TLSA record.
+
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ When performing a text lookup, zero or more records will be returned. Each
+ record is represented by a QDnsTlsAssociationRecord instance.
+
+ The meaning of the fields is defined in \l{RFC 6698}.
+
+ \sa QDnsLookup
+*/
+
+QT_DEFINE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
+
+/*!
+ \enum QDnsTlsAssociationRecord::CertificateUsage
+
+ This enumeration contains valid values for the certificate usage field of
+ TLS Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.1 and RFC 7218 section 2.1. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value CertificateAuthorityConstrait
+ Indicates the record includes an association to a specific Certificate
+ Authority that must be found in the TLS server's certificate chain and
+ must pass PKIX validation.
+
+ \value ServiceCertificateConstraint
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server and must
+ pass PKIX validation.
+
+ \value TrustAnchorAssertion
+ Indicates the record includes an association to a certificate that MUST
+ be used as the ultimate trust anchor to validate the TLS server's
+ certificate and must pass PKIX validation.
+
+ \value DomainIssuedCertificate
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server. PKIX
+ validation is not tested.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PKIX_TA
+ Alias; mnemonic for Public Key Infrastructure Trust Anchor
+
+ \value PKIX_EE
+ Alias; mnemonic for Public Key Infrastructure End Entity
+
+ \value DANE_TA
+ Alias; mnemonic for DNS-based Authentication of Named Entities Trust Anchor
+
+ \value DANE_EE
+ Alias; mnemonic for DNS-based Authentication of Named Entities End Entity
+
+ \value PrivCert
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa certificateUsage()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::Selector
+
+ This enumeration contains valid values for the selector field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.2 and RFC 7218 section 2.2. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value FullCertificate
+ Indicates this record refers to the full certificate in its binary
+ structure form.
+
+ \value SubjectPublicKeyInfo
+ Indicates the record refers to the certificate's subject and public
+ key information, in DER-encoded binary structure form.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value Cert
+ Alias
+
+ \value SPKI
+ Alias
+
+ \value PrivSel
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa selector()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::MatchingType
+
+ This enumeration contains valid values for the matching type field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.3 and RFC 7218 section 2.3. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value Exact
+ Indicates this the certificate or SPKI data is stored verbatim in this
+ record.
+
+ \value Sha256
+ Indicates this a SHA-256 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value Sha512
+ Indicates this a SHA-512 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PrivMatch
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa matchingType()
+*/
+
+/*!
+ Constructs an empty TLS Association record.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord()
+ : d(new QDnsTlsAssociationRecordPrivate)
{
- Q_Q(QDnsLookup);
- if (runnable == q->sender()) {
-#ifdef QDNSLOOKUP_DEBUG
- qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
+}
+
+/*!
+ Constructs a copy of \a other.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Moves the content of \a other into this object.
+ */
+QDnsTlsAssociationRecord &
+QDnsTlsAssociationRecord::operator=(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Destroys this TLS Association record object.
+ */
+QDnsTlsAssociationRecord::~QDnsTlsAssociationRecord() = default;
+
+/*!
+ Returns the name of this record.
+*/
+QString QDnsTlsAssociationRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+quint32 QDnsTlsAssociationRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the certificate usage field for this record.
+ */
+QDnsTlsAssociationRecord::CertificateUsage QDnsTlsAssociationRecord::usage() const
+{
+ return d->usage;
+}
+
+/*!
+ Returns the selector field for this record.
+ */
+QDnsTlsAssociationRecord::Selector QDnsTlsAssociationRecord::selector() const
+{
+ return d->selector;
+}
+
+/*!
+ Returns the match type field for this record.
+ */
+QDnsTlsAssociationRecord::MatchingType QDnsTlsAssociationRecord::matchType() const
+{
+ return d->matchType;
+}
+
+/*!
+ Returns the binary data field for this record. The interpretation of this
+ binary data depends on the three numeric fields provided by
+ certificateUsage(), selector(), and matchType().
+
+ Do note this is a binary field, even for the checksums, similar to what
+ QCyrptographicHash::result() returns.
+ */
+QByteArray QDnsTlsAssociationRecord::value() const
+{
+ return d->value;
+}
+
+static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
+{
+ QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
+ if (label.isEmpty())
+ return QDnsLookupRunnable::EncodedLabel(1, rootDomain);
+
+ QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
+#ifdef Q_OS_WIN
+ return encodedLabel;
+#else
+ return std::move(encodedLabel).toLatin1();
+#endif
+}
+
+inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
+ : requestName(encodeLabel(d->name)),
+ nameserver(d->nameserver),
+ requestType(d->type),
+ port(d->port),
+ protocol(d->protocol)
+{
+ if (port == 0)
+ port = QDnsLookup::defaultPortForProtocol(protocol);
+#if QT_CONFIG(ssl)
+ sslConfiguration = d->sslConfiguration;
#endif
- reply = _reply;
- runnable = nullptr;
- isFinished = true;
- emit q->finished();
- }
}
void QDnsLookupRunnable::run()
@@ -1003,60 +1528,145 @@ void QDnsLookupRunnable::run()
QDnsLookupReply reply;
// Validate input.
- if (requestName.isEmpty()) {
+ if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
reply.error = QDnsLookup::InvalidRequestError;
- reply.errorString = tr("Invalid domain name");
- emit finished(reply);
- return;
+ reply.errorString = QDnsLookup::tr("Invalid domain name");
+ } else {
+ // Perform request.
+ query(&reply);
+
+ // Sort results.
+ qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
+ qt_qdnsservicerecord_sort(reply.serviceRecords);
}
- // Perform request.
- query(requestType, requestName, nameserver, &reply);
+ emit finished(reply);
- // Sort results.
- qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
- qt_qdnsservicerecord_sort(reply.serviceRecords);
+ // maybe print the lookup error as warning
+ switch (reply.error) {
+ case QDnsLookup::NoError:
+ case QDnsLookup::OperationCancelledError:
+ case QDnsLookup::NotFoundError:
+ case QDnsLookup::ServerFailureError:
+ case QDnsLookup::ServerRefusedError:
+ case QDnsLookup::TimeoutError:
+ break; // no warning for these
+
+ case QDnsLookup::ResolverError:
+ case QDnsLookup::InvalidRequestError:
+ case QDnsLookup::InvalidReplyError:
+ qCWarning(lcDnsLookup()).nospace()
+ << "DNS lookup failed (" << reply.error << "): "
+ << qUtf16Printable(reply.errorString)
+ << "; request was " << this; // continues below
+ }
+}
- emit finished(reply);
+inline QDebug operator<<(QDebug &d, QDnsLookupRunnable *r)
+{
+ // continued: print the information about the request
+ d << r->requestName.left(MaxDomainNameLength);
+ if (r->requestName.size() > MaxDomainNameLength)
+ d << "... (truncated)";
+ d << " type " << r->requestType;
+ if (!r->nameserver.isNull()) {
+ d << " to nameserver " << qUtf16Printable(r->nameserver.toString())
+ << " port " << (r->port ? r->port : QDnsLookup::defaultPortForProtocol(r->protocol));
+ switch (r->protocol) {
+ case QDnsLookup::Standard:
+ break;
+ case QDnsLookup::DnsOverTls:
+ d << " (TLS)";
+ }
+ }
+ return d;
}
-#if QT_CONFIG(thread)
-QDnsLookupThreadPool::QDnsLookupThreadPool()
- : signalsConnected(false)
+#if QT_CONFIG(ssl)
+static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
+static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
+static constexpr quint8 DnsAuthenticDataBit = 0x20;
+
+static int makeReplyErrorFromSocket(QDnsLookupReply *reply, const QAbstractSocket *socket)
{
- // Run up to 5 lookups in parallel.
- setMaxThreadCount(5);
+ QDnsLookup::Error error = [&] {
+ switch (socket->error()) {
+ case QAbstractSocket::SocketTimeoutError:
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ return QDnsLookup::TimeoutError;
+ default:
+ return QDnsLookup::ResolverError;
+ }
+ }();
+ reply->setError(error, socket->errorString());
+ return false;
}
-void QDnsLookupThreadPool::start(QRunnable *runnable)
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
{
- // Ensure threads complete at application destruction.
- if (!signalsConnected) {
- QMutexLocker signalsLocker(&signalsMutex);
- if (!signalsConnected) {
- QCoreApplication *app = QCoreApplication::instance();
- if (!app) {
- qWarning("QDnsLookup requires a QCoreApplication");
- delete runnable;
- return;
- }
+ QSslSocket socket;
+ socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
+
+# if QT_CONFIG(networkproxy)
+ socket.setProtocolTag("domain-s"_L1);
+# endif
+
+ // Request the name server attempt to authenticate the reply.
+ query[3] |= DnsAuthenticDataBit;
- moveToThread(app->thread());
- connect(app, SIGNAL(destroyed()),
- SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
- signalsConnected = true;
+ do {
+ quint16 size = qToBigEndian<quint16>(query.size());
+ QDeadlineTimer timeout(DnsOverTlsTimeout);
+
+ socket.connectToHostEncrypted(nameserver.toString(), port);
+ socket.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ socket.write(reinterpret_cast<const char *>(query.data()), query.size());
+ if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
+ break;
+
+ reply->sslConfiguration = socket.sslConfiguration();
+
+ // accumulate reply
+ auto waitForBytes = [&](void *buffer, int count) {
+ int remaining = timeout.remainingTime();
+ while (remaining >= 0 && socket.bytesAvailable() < count) {
+ if (!socket.waitForReadyRead(remaining))
+ return false;
+ }
+ return socket.read(static_cast<char *>(buffer), count) == count;
+ };
+ if (!waitForBytes(&size, sizeof(size)))
+ break;
+
+ // note: strictly speaking, we're allocating memory based on untrusted data
+ // but in practice, due to limited range of the data type (16 bits),
+ // the maximum allocation is small.
+ size = qFromBigEndian(size);
+ response.resize(size);
+ if (waitForBytes(response.data(), size)) {
+ // check if the AD bit is set; we'll trust it over TLS requests
+ if (size >= 4)
+ reply->authenticData = response[3] & DnsAuthenticDataBit;
+ return true;
}
- }
+ } while (false);
- QThreadPool::start(runnable);
+ // handle errors
+ return makeReplyErrorFromSocket(reply, &socket);
}
-
-void QDnsLookupThreadPool::_q_applicationDestroyed()
+#else
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
{
- waitForDone();
- signalsConnected = false;
+ Q_UNUSED(query)
+ Q_UNUSED(response)
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("SSL/TLS support not present"));
+ return false;
}
-#endif // QT_CONFIG(thread)
+#endif
+
QT_END_NAMESPACE
#include "moc_qdnslookup.cpp"
+#include "moc_qdnslookup_p.cpp"
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index 110a74da44..8d21e99c84 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDNSLOOKUP_H
#define QDNSLOOKUP_H
@@ -44,8 +8,8 @@
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qshareddata.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qstring.h>
+#include <QtCore/qproperty.h>
QT_REQUIRE_CONFIG(dnslookup);
@@ -58,6 +22,10 @@ class QDnsHostAddressRecordPrivate;
class QDnsMailExchangeRecordPrivate;
class QDnsServiceRecordPrivate;
class QDnsTextRecordPrivate;
+class QDnsTlsAssociationRecordPrivate;
+class QSslConfiguration;
+
+QT_DECLARE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
class Q_NETWORK_EXPORT QDnsDomainNameRecord
{
@@ -68,7 +36,7 @@ public:
QDnsDomainNameRecord &operator=(const QDnsDomainNameRecord &other);
~QDnsDomainNameRecord();
- void swap(QDnsDomainNameRecord &other) noexcept { qSwap(d, other.d); }
+ void swap(QDnsDomainNameRecord &other) noexcept { d.swap(other.d); }
QString name() const;
quint32 timeToLive() const;
@@ -90,7 +58,7 @@ public:
QDnsHostAddressRecord &operator=(const QDnsHostAddressRecord &other);
~QDnsHostAddressRecord();
- void swap(QDnsHostAddressRecord &other) noexcept { qSwap(d, other.d); }
+ void swap(QDnsHostAddressRecord &other) noexcept { d.swap(other.d); }
QString name() const;
quint32 timeToLive() const;
@@ -112,7 +80,7 @@ public:
QDnsMailExchangeRecord &operator=(const QDnsMailExchangeRecord &other);
~QDnsMailExchangeRecord();
- void swap(QDnsMailExchangeRecord &other) noexcept { qSwap(d, other.d); }
+ void swap(QDnsMailExchangeRecord &other) noexcept { d.swap(other.d); }
QString exchange() const;
QString name() const;
@@ -135,7 +103,7 @@ public:
QDnsServiceRecord &operator=(const QDnsServiceRecord &other);
~QDnsServiceRecord();
- void swap(QDnsServiceRecord &other) noexcept { qSwap(d, other.d); }
+ void swap(QDnsServiceRecord &other) noexcept { d.swap(other.d); }
QString name() const;
quint16 port() const;
@@ -160,7 +128,7 @@ public:
QDnsTextRecord &operator=(const QDnsTextRecord &other);
~QDnsTextRecord();
- void swap(QDnsTextRecord &other) noexcept { qSwap(d, other.d); }
+ void swap(QDnsTextRecord &other) noexcept { d.swap(other.d); }
QString name() const;
quint32 timeToLive() const;
@@ -173,14 +141,92 @@ private:
Q_DECLARE_SHARED(QDnsTextRecord)
+class Q_NETWORK_EXPORT QDnsTlsAssociationRecord
+{
+ Q_GADGET
+public:
+ enum class CertificateUsage : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#certificate-usages
+ // RFC 6698
+ CertificateAuthorityConstrait = 0,
+ ServiceCertificateConstraint = 1,
+ TrustAnchorAssertion = 2,
+ DomainIssuedCertificate = 3,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ PKIX_TA = 0,
+ PKIX_EE = 1,
+ DANE_TA = 2,
+ DANE_EE = 3,
+ PrivCert = 255,
+ };
+ Q_ENUM(CertificateUsage)
+
+ enum class Selector : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#selectors
+ // RFC 6698
+ FullCertificate = 0,
+ SubjectPublicKeyInfo = 1,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ Cert = FullCertificate,
+ SPKI = SubjectPublicKeyInfo,
+ PrivSel = PrivateUse,
+ };
+ Q_ENUM(Selector)
+
+ enum class MatchingType : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types
+ // RFC 6698
+ Exact = 0,
+ Sha256 = 1,
+ Sha512 = 2,
+ PrivateUse = 255,
+ PrivMatch = PrivateUse,
+ };
+ Q_ENUM(MatchingType)
+
+ QDnsTlsAssociationRecord();
+ QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other);
+ QDnsTlsAssociationRecord(QDnsTlsAssociationRecord &&other)
+ : d(std::move(other.d))
+ {}
+ QDnsTlsAssociationRecord &operator=(QDnsTlsAssociationRecord &&other) noexcept { swap(other); return *this; }
+ QDnsTlsAssociationRecord &operator=(const QDnsTlsAssociationRecord &other);
+ ~QDnsTlsAssociationRecord();
+
+ void swap(QDnsTlsAssociationRecord &other) noexcept { d.swap(other.d); }
+
+ QString name() const;
+ quint32 timeToLive() const;
+ CertificateUsage usage() const;
+ Selector selector() const;
+ MatchingType matchType() const;
+ QByteArray value() const;
+
+private:
+ QSharedDataPointer<QDnsTlsAssociationRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+Q_DECLARE_SHARED(QDnsTlsAssociationRecord)
+
class Q_NETWORK_EXPORT QDnsLookup : public QObject
{
Q_OBJECT
Q_PROPERTY(Error error READ error NOTIFY finished)
+ Q_PROPERTY(bool authenticData READ isAuthenticData NOTIFY finished)
Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
- Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
- Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
- Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged BINDABLE bindableName)
+ Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged BINDABLE bindableType)
+ Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged
+ BINDABLE bindableNameserver)
+ Q_PROPERTY(quint16 nameserverPort READ nameserverPort WRITE setNameserverPort
+ NOTIFY nameserverPortChanged BINDABLE bindableNameserverPort)
+ Q_PROPERTY(Protocol nameserverProtocol READ nameserverProtocol WRITE setNameserverProtocol
+ NOTIFY nameserverProtocolChanged BINDABLE bindableNameserverProtocol)
public:
enum Error
@@ -192,7 +238,8 @@ public:
InvalidReplyError,
ServerFailureError,
ServerRefusedError,
- NotFoundError
+ NotFoundError,
+ TimeoutError,
};
Q_ENUM(Error)
@@ -206,27 +253,51 @@ public:
NS = 2,
PTR = 12,
SRV = 33,
+ TLSA = 52,
TXT = 16
};
Q_ENUM(Type)
+ enum Protocol : quint8 {
+ Standard = 0,
+ DnsOverTls,
+ };
+ Q_ENUM(Protocol)
+
explicit QDnsLookup(QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port,
+ QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, Protocol protocol, const QHostAddress &nameserver,
+ quint16 port = 0, QObject *parent = nullptr);
~QDnsLookup();
+ bool isAuthenticData() const;
Error error() const;
QString errorString() const;
bool isFinished() const;
QString name() const;
void setName(const QString &name);
+ QBindable<QString> bindableName();
Type type() const;
void setType(QDnsLookup::Type);
+ QBindable<Type> bindableType();
QHostAddress nameserver() const;
void setNameserver(const QHostAddress &nameserver);
+ QBindable<QHostAddress> bindableNameserver();
+ quint16 nameserverPort() const;
+ void setNameserverPort(quint16 port);
+ QBindable<quint16> bindableNameserverPort();
+ Protocol nameserverProtocol() const;
+ void setNameserverProtocol(Protocol protocol);
+ QBindable<Protocol> bindableNameserverProtocol();
+ void setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port = 0);
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setNameserver(const QHostAddress &nameserver, quint16 port);
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
QList<QDnsHostAddressRecord> hostAddressRecords() const;
@@ -235,7 +306,15 @@ public:
QList<QDnsDomainNameRecord> pointerRecords() const;
QList<QDnsServiceRecord> serviceRecords() const;
QList<QDnsTextRecord> textRecords() const;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords() const;
+
+#if QT_CONFIG(ssl)
+ void setSslConfiguration(const QSslConfiguration &sslConfiguration);
+ QSslConfiguration sslConfiguration() const;
+#endif
+ static bool isProtocolSupported(Protocol protocol);
+ static quint16 defaultPortForProtocol(Protocol protocol) noexcept Q_DECL_CONST_FUNCTION;
public Q_SLOTS:
void abort();
@@ -246,12 +325,20 @@ Q_SIGNALS:
void nameChanged(const QString &name);
void typeChanged(Type type);
void nameserverChanged(const QHostAddress &nameserver);
+ void nameserverPortChanged(quint16 port);
+ void nameserverProtocolChanged(Protocol protocol);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
- Q_PRIVATE_SLOT(d_func(), void _q_lookupFinished(const QDnsLookupReply &reply))
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+{
+ setNameserver(Standard, nameserver, port);
+}
+#endif
+
QT_END_NAMESPACE
#endif // QDNSLOOKUP_H
diff --git a/src/network/kernel/qdnslookup_android.cpp b/src/network/kernel/qdnslookup_android.cpp
deleted file mode 100644
index 131fc56298..0000000000
--- a/src/network/kernel/qdnslookup_android.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qdnslookup_p.h"
-
-QT_BEGIN_NAMESPACE
-
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType);
- Q_UNUSED(requestName);
- Q_UNUSED(nameserver);
- Q_UNUSED(reply);
- qWarning("Not yet supported on Android");
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Not yet supported on Android");
- return;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_dummy.cpp b/src/network/kernel/qdnslookup_dummy.cpp
new file mode 100644
index 0000000000..6cc6ed92c5
--- /dev/null
+++ b/src/network/kernel/qdnslookup_dummy.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qdnslookup_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Not yet supported on this OS");
+ return;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 8c3c2ed3e1..1f32b4ee4f 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -1,41 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDNSLOOKUP_P_H
#define QDNSLOOKUP_P_H
@@ -54,13 +19,17 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qmutex.h"
#include "QtCore/qrunnable.h"
-#include "QtCore/qsharedpointer.h"
#if QT_CONFIG(thread)
#include "QtCore/qthreadpool.h"
#endif
#include "QtNetwork/qdnslookup.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
+#include "private/qurl_p.h"
+
+#if QT_CONFIG(ssl)
+# include "qsslconfiguration.h"
+#endif
QT_REQUIRE_CONFIG(dnslookup);
@@ -68,16 +37,18 @@ QT_BEGIN_NAMESPACE
//#define QDNSLOOKUP_DEBUG
+constexpr qsizetype MaxDomainNameLength = 255;
+constexpr quint16 DnsPort = 53;
+constexpr quint16 DnsOverTlsPort = 853;
+
class QDnsLookupRunnable;
+QDebug operator<<(QDebug &, QDnsLookupRunnable *);
class QDnsLookupReply
{
public:
- QDnsLookupReply()
- : error(QDnsLookup::NoError)
- { }
-
- QDnsLookup::Error error;
+ QDnsLookup::Error error = QDnsLookup::NoError;
+ bool authenticData = false;
QString errorString;
QList<QDnsDomainNameRecord> canonicalNameRecords;
@@ -86,28 +57,139 @@ public:
QList<QDnsDomainNameRecord> nameServerRecords;
QList<QDnsDomainNameRecord> pointerRecords;
QList<QDnsServiceRecord> serviceRecords;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords;
QList<QDnsTextRecord> textRecords;
+
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+
+ // helper methods
+ void setError(QDnsLookup::Error err, QString &&msg)
+ {
+ error = err;
+ errorString = std::move(msg);
+ }
+
+ void makeResolverSystemError(int code = -1)
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::ResolverError, qt_error_string(code));
+ }
+
+ void makeTimeoutError()
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::TimeoutError, QDnsLookup::tr("Request timed out"));
+ }
+
+ void makeDnsRcodeError(quint8 rcode)
+ {
+ Q_ASSERT(allAreEmpty());
+ switch (rcode) {
+ case 1: // FORMERR
+ error = QDnsLookup::InvalidRequestError;
+ errorString = QDnsLookup::tr("Server could not process query");
+ return;
+ case 2: // SERVFAIL
+ case 4: // NOTIMP
+ error = QDnsLookup::ServerFailureError;
+ errorString = QDnsLookup::tr("Server failure");
+ return;
+ case 3: // NXDOMAIN
+ error = QDnsLookup::NotFoundError;
+ errorString = QDnsLookup::tr("Non existent domain");
+ return;
+ case 5: // REFUSED
+ error = QDnsLookup::ServerRefusedError;
+ errorString = QDnsLookup::tr("Server refused to answer");
+ return;
+ default:
+ error = QDnsLookup::InvalidReplyError;
+ errorString = QDnsLookup::tr("Invalid reply received (rcode %1)")
+ .arg(rcode);
+ return;
+ }
+ }
+
+ void makeInvalidReplyError(QString &&msg = QString())
+ {
+ if (msg.isEmpty())
+ msg = QDnsLookup::tr("Invalid reply received");
+ else
+ msg = QDnsLookup::tr("Invalid reply received (%1)").arg(std::move(msg));
+ *this = QDnsLookupReply(); // empty our lists
+ setError(QDnsLookup::InvalidReplyError, std::move(msg));
+ }
+
+private:
+ bool allAreEmpty() const
+ {
+ return canonicalNameRecords.isEmpty()
+ && hostAddressRecords.isEmpty()
+ && mailExchangeRecords.isEmpty()
+ && nameServerRecords.isEmpty()
+ && pointerRecords.isEmpty()
+ && serviceRecords.isEmpty()
+ && tlsAssociationRecords.isEmpty()
+ && textRecords.isEmpty();
+ }
};
class QDnsLookupPrivate : public QObjectPrivate
{
public:
QDnsLookupPrivate()
- : isFinished(false)
- , type(QDnsLookup::A)
- , runnable(nullptr)
+ : type(QDnsLookup::A)
+ , port(0)
+ , protocol(QDnsLookup::Standard)
{ }
- void _q_lookupFinished(const QDnsLookupReply &reply);
+ void nameChanged()
+ {
+ emit q_func()->nameChanged(name);
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QString, name,
+ &QDnsLookupPrivate::nameChanged);
- static const char *msgNoIpV6NameServerAdresses;
+ void nameserverChanged()
+ {
+ emit q_func()->nameserverChanged(nameserver);
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
+ &QDnsLookupPrivate::nameserverChanged);
+
+ void typeChanged()
+ {
+ emit q_func()->typeChanged(type);
+ }
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Type,
+ type, &QDnsLookupPrivate::typeChanged);
+
+ void nameserverPortChanged()
+ {
+ emit q_func()->nameserverPortChanged(port);
+ }
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
+ port, &QDnsLookupPrivate::nameserverPortChanged);
+
+ void nameserverProtocolChanged()
+ {
+ emit q_func()->nameserverProtocolChanged(protocol);
+ }
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Protocol,
+ protocol, &QDnsLookupPrivate::nameserverProtocolChanged);
- bool isFinished;
- QString name;
- QDnsLookup::Type type;
- QHostAddress nameserver;
QDnsLookupReply reply;
- QDnsLookupRunnable *runnable;
+ QDnsLookupRunnable *runnable = nullptr;
+ bool isFinished = false;
+
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
Q_DECLARE_PUBLIC(QDnsLookup)
};
@@ -117,40 +199,40 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
- QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name, const QHostAddress &nameserver)
- : requestType(type)
- , requestName(name)
- , nameserver(nameserver)
- { }
+#ifdef Q_OS_WIN
+ using EncodedLabel = QString;
+#else
+ using EncodedLabel = QByteArray;
+#endif
+ // minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
+ static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
+ using ReplyBuffer = QVarLengthArray<unsigned char, ReplyBufferSize>;
+
+ QDnsLookupRunnable(const QDnsLookupPrivate *d);
void run() override;
+ bool sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query, ReplyBuffer &response);
signals:
void finished(const QDnsLookupReply &reply);
private:
- static void query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply);
- QDnsLookup::Type requestType;
- QByteArray requestName;
- QHostAddress nameserver;
-};
-
-#if QT_CONFIG(thread)
-class QDnsLookupThreadPool : public QThreadPool
-{
- Q_OBJECT
-
-public:
- QDnsLookupThreadPool();
- void start(QRunnable *runnable);
+ template <typename T> static QString decodeLabel(T encodedLabel)
+ {
+ return qt_ACE_do(encodedLabel.toString(), NormalizeAce, ForbidLeadingDot);
+ }
+ void query(QDnsLookupReply *reply);
-private slots:
- void _q_applicationDestroyed();
+ EncodedLabel requestName;
+ QHostAddress nameserver;
+ QDnsLookup::Type requestType;
+ quint16 port;
+ QDnsLookup::Protocol protocol;
-private:
- QMutex signalsMutex;
- bool signalsConnected;
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+ friend QDebug operator<<(QDebug &, QDnsLookupRunnable *);
};
-#endif // QT_CONFIG(thread)
class QDnsRecordPrivate : public QSharedData
{
@@ -216,8 +298,15 @@ public:
QList<QByteArray> values;
};
-QT_END_NAMESPACE
+class QDnsTlsAssociationRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsTlsAssociationRecord::CertificateUsage usage;
+ QDnsTlsAssociationRecord::Selector selector;
+ QDnsTlsAssociationRecord::MatchingType matchType;
+ QByteArray value;
+};
-Q_DECLARE_METATYPE(QDnsLookupReply)
+QT_END_NAMESPACE
#endif // QDNSLOOKUP_P_H
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 12b40fc35d..9de073b781 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -1,421 +1,448 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup_p.h"
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qvarlengtharray.h>
+#include <qendian.h>
#include <qscopedpointer.h>
+#include <qspan.h>
#include <qurl.h>
-#include <private/qnativesocketengine_p.h>
+#include <qvarlengtharray.h>
+#include <private/qnativesocketengine_p.h> // for setSockAddr
+#include <private/qtnetwork-config_p.h>
+
+QT_REQUIRE_CONFIG(libresolv);
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
-#if !defined(Q_OS_OPENBSD)
+#if __has_include(<arpa/nameser_compat.h>)
# include <arpa/nameser_compat.h>
#endif
+#include <errno.h>
#include <resolv.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
-#endif
+#include <array>
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef T_OPT
+// the older arpa/nameser_compat.h wasn't updated between 1999 and 2016 in glibc
+# define T_OPT ns_t_opt
#endif
-#include <cstring>
-
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(library)
+using namespace Qt::StringLiterals;
+using ReplyBuffer = QDnsLookupRunnable::ReplyBuffer;
+
+// https://www.rfc-editor.org/rfc/rfc6891
+static constexpr unsigned char Edns0Record[] = {
+ 0x00, // root label
+ T_OPT >> 8, T_OPT & 0xff, // type OPT
+ ReplyBuffer::PreallocatedSize >> 8, ReplyBuffer::PreallocatedSize & 0xff, // payload size
+ NOERROR, // extended rcode
+ 0, // version
+ 0x00, 0x00, // flags
+ 0x00, 0x00, // option length
+};
-#if defined(Q_OS_OPENBSD)
-typedef struct __res_state* res_state;
-#endif
-typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
-static dn_expand_proto local_dn_expand = nullptr;
-typedef void (*res_nclose_proto)(res_state);
-static res_nclose_proto local_res_nclose = nullptr;
-typedef int (*res_ninit_proto)(res_state);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
-static res_nquery_proto local_res_nquery = nullptr;
-
-// Custom deleter to close resolver state.
-
-struct QDnsLookupStateDeleter
+// maximum length of a EDNS0 query with a 255-character domain (rounded up to 16)
+static constexpr qsizetype QueryBufferSize =
+ HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1 + sizeof(Edns0Record);
+using QueryBuffer = std::array<unsigned char, (QueryBufferSize + 15) / 16 * 16>;
+
+namespace {
+struct QDnsCachedName
{
- static inline void cleanup(struct __res_state *pointer)
- {
- local_res_nclose(pointer);
- }
+ QString name;
+ int code = 0;
+ QDnsCachedName(const QString &name, int code) : name(name), code(code) {}
};
+}
+Q_DECLARE_TYPEINFO(QDnsCachedName, Q_RELOCATABLE_TYPE);
+using Cache = QList<QDnsCachedName>; // QHash or QMap are overkill
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
+#if QT_CONFIG(res_setservers)
+// https://www.ibm.com/docs/en/i/7.3?topic=ssw_ibm_i_73/apis/ressetservers.html
+// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
+ union res_sockaddr_union u;
+ setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
+ res_setservers(state, &u, 1);
+ return true;
+}
#else
- return nullptr;
-#endif
+template <typename T> void setNsMap(T &ext, std::enable_if_t<sizeof(T::nsmap) != 0, uint16_t> v)
+{
+ // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
+ // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
+ // Unneeded since glibc 2.22 (2015), but doesn't hurt to set it
+ // See: https://sourceware.org/git/?p=glibc.git;a=commit;h=2212c1420c92a33b0e0bd9a34938c9814a56c0f7
+ ext.nsmap[0] = v;
}
-
-static bool resolveLibraryInternal()
+template <typename T> void setNsMap(T &, ...)
{
- QLibrary lib;
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
-#endif
- {
- lib.setFileName(QLatin1String("resolv"));
- lib.load();
- }
+ // fallback
+}
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "__dn_expand"));
- if (!local_dn_expand)
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "dn_expand"));
+template <bool Condition>
+using EnableIfIPv6 = std::enable_if_t<Condition, const QHostAddress *>;
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_9_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
+template <typename State>
+bool setIpv6NameServer(State *state,
+ EnableIfIPv6<sizeof(std::declval<State>()._u._ext.nsaddrs) != 0> addr,
+ quint16 port)
+{
+ // glibc-like API to set IPv6 name servers
+ struct sockaddr_in6 *ns = state->_u._ext.nsaddrs[0];
+
+ // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
+ if (!ns) {
+ // Memory allocated here will be free()'d in res_close() as we
+ // have done res_init() above.
+ ns = static_cast<struct sockaddr_in6*>(calloc(1, sizeof(struct sockaddr_in6)));
+ Q_CHECK_PTR(ns);
+ state->_u._ext.nsaddrs[0] = ns;
+ }
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_9_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
+ setNsMap(state->_u._ext, MAXNS + 1);
+ state->_u._ext.nscount6 = 1;
+ setSockaddr(ns, *addr, port);
+ return true;
+}
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "__res_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_9_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_nquery"));
+template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
+{
+ // fallback
+ return false;
+}
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
+{
+ state->nscount = 1;
+ state->nsaddr_list[0].sin_family = AF_UNSPEC;
+ if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
+ return setIpv6NameServer(state, &nameserver, port);
+ setSockaddr(&state->nsaddr_list[0], nameserver, port);
return true;
}
-Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal()))
+#endif // !QT_CONFIG(res_setservers)
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+static int
+prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_rcode type)
{
- // Load dn_expand, res_ninit and res_nquery on demand.
- resolveLibrary();
+ // Create header and our query
+ int queryLength = res_nmkquery(state, QUERY, label, C_IN, type, nullptr, 0, nullptr,
+ buffer.data(), buffer.size());
+ Q_ASSERT(queryLength < int(buffer.size()));
+ if (Q_UNLIKELY(queryLength < 0))
+ return queryLength;
+
+ // Append EDNS0 record and set the number of additional RRs to 1
+ Q_ASSERT(queryLength + sizeof(Edns0Record) < buffer.size());
+ std::copy_n(std::begin(Edns0Record), sizeof(Edns0Record), buffer.begin() + queryLength);
+ reinterpret_cast<HEADER *>(buffer.data())->arcount = qToBigEndian<quint16>(1);
+
+ return queryLength + sizeof(Edns0Record);
+}
- // If dn_expand, res_ninit or res_nquery is missing, fail.
- if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver functions not found");
- return;
+static int sendStandardDns(QDnsLookupReply *reply, res_state state, QSpan<unsigned char> qbuffer,
+ ReplyBuffer &buffer, const QHostAddress &nameserver, quint16 port)
+{
+ // Check if a nameserver was set. If so, use it.
+ if (!nameserver.isNull()) {
+ if (!applyNameServer(state, nameserver, port)) {
+ reply->setError(QDnsLookup::ResolverError,
+ QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
+ return -1;
+ }
+
+ // Request the name server attempt to authenticate the reply.
+ reinterpret_cast<HEADER *>(buffer.data())->ad = true;
+
+#ifdef RES_TRUSTAD
+ // Need to set this option even though we set the AD bit, otherwise
+ // glibc turns it off.
+ state->options |= RES_TRUSTAD;
+#endif
}
- // Initialize state.
- struct __res_state state;
- std::memset(&state, 0, sizeof(state));
- if (local_res_ninit(&state) < 0) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver initialization failed");
- return;
+ auto attemptToSend = [&]() {
+ std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
+ int responseLength = res_nsend(state, qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
+ if (responseLength >= 0)
+ return responseLength; // success
+
+ // libresolv uses ETIMEDOUT for resolver errors ("no answer")
+ if (errno == ECONNREFUSED)
+ reply->setError(QDnsLookup::ServerRefusedError, qt_error_string());
+ else if (errno != ETIMEDOUT)
+ reply->makeResolverSystemError(); // some other error
+
+ auto query = reinterpret_cast<HEADER *>(qbuffer.data());
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (query->id == header->id && header->qr)
+ reply->makeDnsRcodeError(header->rcode);
+ else
+ reply->makeTimeoutError(); // must really be a timeout
+ return -1;
+ };
+
+ // strictly use UDP, we'll deal with truncated replies ourselves
+ state->options |= RES_IGNTC;
+ int responseLength = attemptToSend();
+ if (responseLength < 0)
+ return responseLength;
+
+ // check if we need to use the virtual circuit (TCP)
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (header->rcode == NOERROR && header->tc) {
+ // yes, increase our buffer size
+ buffer.resize(std::numeric_limits<quint16>::max());
+ header = reinterpret_cast<HEADER *>(buffer.data());
+
+ // remove the EDNS record in the query
+ reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
+ qbuffer = qbuffer.first(qbuffer.size() - sizeof(Edns0Record));
+
+ // send using the virtual circuit
+ state->options |= RES_USEVC;
+ responseLength = attemptToSend();
+ if (Q_UNLIKELY(responseLength > buffer.size())) {
+ // Ok, we give up.
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
+ return -1;
+ }
}
- //Check if a nameserver was set. If so, use it
- if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- state.nsaddr_list[0].sin_addr.s_addr = htonl(nameserver.toIPv4Address());
- state.nscount = 1;
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
-#if defined(Q_OS_LINUX)
- struct sockaddr_in6 *ns;
- ns = state._u._ext.nsaddrs[0];
- // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
- if (!ns) {
- // Memory allocated here will be free'd in res_close() as we
- // have done res_init() above.
- ns = (struct sockaddr_in6*) calloc(1, sizeof(struct sockaddr_in6));
- Q_CHECK_PTR(ns);
- state._u._ext.nsaddrs[0] = ns;
- }
-#ifndef __UCLIBC__
- // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
- // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
- state._u._ext.nsmap[0] = MAXNS + 1;
-#endif
- state._u._ext.nscount6 = 1;
- ns->sin6_family = AF_INET6;
- ns->sin6_port = htons(53);
- SetSALen::set(ns, sizeof(*ns));
-
- Q_IPV6ADDR ipv6Address = nameserver.toIPv6Address();
- for (int i=0; i<16; i++) {
- ns->sin6_addr.s6_addr[i] = ipv6Address[i];
- }
-#else
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
+ // We only trust the AD bit in the reply if we're querying a custom name
+ // server or if we can tell the system administrator configured the resolver
+ // to trust replies.
+#ifndef RES_TRUSTAD
+ if (nameserver.isNull())
+ header->ad = false;
#endif
- }
+ reply->authenticData = header->ad;
+
+ return responseLength;
+}
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ // Initialize state.
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) < 0) {
+ int error = errno;
+ qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
+ return reply->makeResolverSystemError(error);
}
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
#endif
- QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
+
+ // Prepare the DNS query.
+ QueryBuffer qbuffer;
+ int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
+ if (Q_UNLIKELY(queryLength < 0))
+ return reply->makeResolverSystemError();
// Perform DNS query.
- QVarLengthArray<unsigned char, PACKETSZ> buffer(PACKETSZ);
- std::memset(buffer.data(), 0, buffer.size());
- int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
- if (Q_UNLIKELY(responseLength > PACKETSZ)) {
- buffer.resize(responseLength);
- std::memset(buffer.data(), 0, buffer.size());
- responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
- if (Q_UNLIKELY(responseLength > buffer.size())) {
- // Ok, we give up.
- reply->error = QDnsLookup::ResolverError;
- reply->errorString.clear(); // We cannot be more specific, alas.
+ ReplyBuffer buffer(ReplyBufferSize);
+ int responseLength = -1;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ responseLength = sendStandardDns(reply, &state, qbuffer, buffer, nameserver, port);
+ break;
+ case QDnsLookup::DnsOverTls:
+ if (!sendDnsOverTls(reply, qbuffer, buffer))
return;
- }
+ responseLength = buffer.size();
+ break;
}
- unsigned char *response = buffer.data();
- // Check the response header. Though res_nquery returns -1 as a
- // responseLength in case of error, we still can extract the
- // exact error code from the response.
- HEADER *header = (HEADER*)response;
- const int answerCount = ntohs(header->ancount);
- switch (header->rcode) {
- case NOERROR:
- break;
- case FORMERR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
+ if (responseLength < 0)
return;
- case SERVFAIL:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
- return;
- case NXDOMAIN:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
// Check the reply is valid.
- if (responseLength < int(sizeof(HEADER))) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
+ if (responseLength < int(sizeof(HEADER)))
+ return reply->makeInvalidReplyError();
- // Skip the query host, type (2 bytes) and class (2 bytes).
- char host[PACKETSZ], answer[PACKETSZ];
- unsigned char *p = response + sizeof(HEADER);
- int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
- return;
+ // Parse the reply.
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (header->rcode)
+ return reply->makeDnsRcodeError(header->rcode);
+
+ qptrdiff offset = sizeof(HEADER);
+ unsigned char *response = buffer.data();
+ int status;
+
+ auto expandHost = [&, cache = Cache{}](qptrdiff offset) mutable {
+ if (uchar n = response[offset]; n & NS_CMPRSFLGS) {
+ // compressed name, see if we already have it cached
+ if (offset + 1 < responseLength) {
+ int id = ((n & ~NS_CMPRSFLGS) << 8) | response[offset + 1];
+ auto it = std::find_if(cache.constBegin(), cache.constEnd(),
+ [id](const QDnsCachedName &n) { return n.code == id; });
+ if (it != cache.constEnd()) {
+ status = 2;
+ return it->name;
+ }
+ }
+ }
+
+ // uncached, expand it
+ char host[MAXCDNAME + 1];
+ status = dn_expand(response, response + responseLength, response + offset,
+ host, sizeof(host));
+ if (status >= 0)
+ return cache.emplaceBack(decodeLabel(QLatin1StringView(host)), offset).name;
+
+ // failed
+ reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
+ return QString();
+ };
+
+ if (ntohs(header->qdcount) == 1) {
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ expandHost(offset);
+ if (status < 0)
+ return;
+ if (offset + status + 4 > responseLength)
+ header->qdcount = 0xffff; // invalid reply below
+ else
+ offset += status + 4;
}
- p += status + 4;
+ if (ntohs(header->qdcount) > 1)
+ return reply->makeInvalidReplyError();
// Extract results.
+ const int answerCount = ntohs(header->ancount);
int answerIndex = 0;
- while ((p < response + responseLength) && (answerIndex < answerCount)) {
- status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
+ while ((offset < responseLength) && (answerIndex < answerCount)) {
+ const QString name = expandHost(offset);
+ if (status < 0)
return;
- }
- const QString name = QUrl::fromAce(host);
- p += status;
- const quint16 type = (p[0] << 8) | p[1];
- p += 2; // RR type
- p += 2; // RR class
- const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
- p += 4;
- const quint16 size = (p[0] << 8) | p[1];
- p += 2;
+ offset += status;
+ if (offset + RRFIXEDSZ > responseLength) {
+ // probably just a truncated reply, return what we have
+ return;
+ }
+ const quint16 type = qFromBigEndian<quint16>(response + offset);
+ const qint16 rrclass = qFromBigEndian<quint16>(response + offset + 2);
+ const quint32 ttl = qFromBigEndian<quint32>(response + offset + 4);
+ const quint16 size = qFromBigEndian<quint16>(response + offset + 8);
+ offset += RRFIXEDSZ;
+ if (offset + size > responseLength)
+ return; // truncated
+ if (rrclass != C_IN)
+ continue;
if (type == QDnsLookup::A) {
- if (size != 4) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv4 address record");
- return;
- }
- const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ if (size != 4)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv4 address record"));
+ const quint32 addr = qFromBigEndian<quint32>(response + offset);
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QHostAddress(addr);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::AAAA) {
- if (size != 16) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv6 address record");
- return;
- }
+ if (size != 16)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv6 address record"));
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QHostAddress(p);
+ record.d->value = QHostAddress(response + offset);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::CNAME) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid canonical name record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid canonical name record"));
reply->canonicalNameRecords.append(record);
} else if (type == QDnsLookup::NS) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid name server record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid name server record"));
reply->nameServerRecords.append(record);
} else if (type == QDnsLookup::PTR) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid pointer record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
reply->pointerRecords.append(record);
} else if (type == QDnsLookup::MX) {
- const quint16 preference = (p[0] << 8) | p[1];
- status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid mail exchange record");
- return;
- }
+ const quint16 preference = qFromBigEndian<quint16>(response + offset);
QDnsMailExchangeRecord record;
- record.d->exchange = QUrl::fromAce(answer);
+ record.d->exchange = expandHost(offset + 2);
record.d->name = name;
record.d->preference = preference;
record.d->timeToLive = ttl;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
reply->mailExchangeRecords.append(record);
} else if (type == QDnsLookup::SRV) {
- const quint16 priority = (p[0] << 8) | p[1];
- const quint16 weight = (p[2] << 8) | p[3];
- const quint16 port = (p[4] << 8) | p[5];
- status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid service record");
- return;
- }
+ if (size < 7)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
+ const quint16 priority = qFromBigEndian<quint16>(response + offset);
+ const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
+ const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(answer);
+ record.d->target = expandHost(offset + 6);
record.d->port = port;
record.d->priority = priority;
record.d->timeToLive = ttl;
record.d->weight = weight;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
reply->serviceRecords.append(record);
+ } else if (type == QDnsLookup::TLSA) {
+ // https://datatracker.ietf.org/doc/html/rfc6698#section-2.1
+ if (size < 3)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid TLS association record"));
+
+ const quint8 usage = response[offset];
+ const quint8 selector = response[offset + 1];
+ const quint8 matchType = response[offset + 2];
+
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(response + offset + 3, response + offset + size);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (type == QDnsLookup::TXT) {
- unsigned char *txt = p;
QDnsTextRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- while (txt < p + size) {
- const unsigned char length = *txt;
+ qptrdiff txt = offset;
+ while (txt < offset + size) {
+ const unsigned char length = response[txt];
txt++;
- if (txt + length > p + size) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid text record");
- return;
- }
- record.d->values << QByteArray((char*)txt, length);
+ if (txt + length > offset + size)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid text record"));
+ record.d->values << QByteArrayView(response + txt, length).toByteArray();
txt += length;
}
reply->textRecords.append(record);
}
- p += size;
+ offset += size;
answerIndex++;
}
}
-#else
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType)
- Q_UNUSED(requestName)
- Q_UNUSED(nameserver)
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver library can't be loaded: No runtime library loading support");
- return;
-}
-
-#endif /* QT_CONFIG(library) */
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
index 262893179c..1b07776db9 100644
--- a/src/network/kernel/qdnslookup_win.cpp
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -1,105 +1,180 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <winsock2.h>
#include "qdnslookup_p.h"
-#include <qurl.h>
+#include <qendian.h>
+#include <private/qnativesocketengine_p.h>
#include <private/qsystemerror_p.h>
+#include <qurl.h>
+#include <qspan.h>
#include <qt_windows.h>
#include <windns.h>
#include <memory.h>
+#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH
+// MinGW headers are missing almost all of this
+typedef struct Qt_DnsAddr {
+ CHAR MaxSa[32];
+ DWORD DnsAddrUserDword[8];
+} DNS_ADDR, *PDNS_ADDR;
+typedef struct Qt_DnsAddrArray {
+ DWORD MaxCount;
+ DWORD AddrCount;
+ DWORD Tag;
+ WORD Family;
+ WORD WordReserved;
+ DWORD Flags;
+ DWORD MatchFlag;
+ DWORD Reserved1;
+ DWORD Reserved2;
+ DNS_ADDR AddrArray[];
+} DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY;
+# ifndef DNS_QUERY_RESULTS_VERSION1
+typedef struct Qt_DNS_QUERY_RESULT {
+ ULONG Version;
+ DNS_STATUS QueryStatus;
+ ULONG64 QueryOptions;
+ PDNS_RECORD pQueryRecords;
+ PVOID Reserved;
+} DNS_QUERY_RESULT, *PDNS_QUERY_RESULT;
+typedef VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext,PDNS_QUERY_RESULT pQueryResults);
+typedef DNS_QUERY_COMPLETION_ROUTINE *PDNS_QUERY_COMPLETION_ROUTINE;
+# endif
+typedef struct Qt_DNS_QUERY_REQUEST {
+ ULONG Version;
+ PCWSTR QueryName;
+ WORD QueryType;
+ ULONG64 QueryOptions;
+ PDNS_ADDR_ARRAY pDnsServerList;
+ ULONG InterfaceIndex;
+ PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback;
+ PVOID pQueryContext;
+} DNS_QUERY_REQUEST, *PDNS_QUERY_REQUEST;
+
+typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it
+extern "C" {
+DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
+ PDNS_QUERY_RESULT pQueryResults,
+ PDNS_QUERY_CANCEL pCancelHandle);
+}
+#endif
+
QT_BEGIN_NAMESPACE
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply,
+ PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
{
- // Perform DNS query.
- PDNS_RECORD dns_records = 0;
- const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
- IP4_ARRAY srvList;
- memset(&srvList, 0, sizeof(IP4_ARRAY));
- if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- // The below code is referenced from: http://support.microsoft.com/kb/831226
- srvList.AddrCount = 1;
- srvList.AddrArray[0] = htonl(nameserver.toIPv4Address());
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
- // For supoprting IPv6 nameserver addresses, we'll need to switch
- // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6
- // address in the nameserver list
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
+ // WinDNS wants MTU - IP Header - UDP header for some reason, in spite
+ // of never needing that much
+ QVarLengthArray<unsigned char, 1472> query(1472);
+
+ auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
+ DWORD dnsBufferSize = query.size();
+ WORD xid = 0;
+ bool recursionDesired = true;
+
+ SetLastError(ERROR_SUCCESS);
+
+ // MinGW winheaders incorrectly declare the third parameter as LPWSTR
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ // let's try reallocating
+ query.resize(dnsBufferSize);
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ return GetLastError();
}
}
- const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL);
- switch (status) {
- case ERROR_SUCCESS:
+
+ // set AD bit: we want to trust this server
+ dnsBuffer->MessageHead.AuthenticatedData = true;
+
+ QDnsLookupRunnable::ReplyBuffer replyBuffer;
+ if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer))
+ return DNS_STATUS(-1); // error set in reply
+
+ // interpret the RCODE in the reply
+ auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
+ DNS_HEADER *header = &response->MessageHead;
+ if (!header->IsResponse)
+ return DNS_ERROR_BAD_PACKET; // not a reply
+
+ // Convert the byte order for the 16-bit quantities in the header, so
+ // DnsExtractRecordsFromMessage can parse the contents.
+ //header->Xid = qFromBigEndian(header->Xid);
+ header->QuestionCount = qFromBigEndian(header->QuestionCount);
+ header->AnswerCount = qFromBigEndian(header->AnswerCount);
+ header->NameServerCount = qFromBigEndian(header->NameServerCount);
+ header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
+
+ results->QueryOptions = request->QueryOptions;
+ return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
+}
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ // Perform DNS query.
+ alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
+ DNS_QUERY_REQUEST request = {};
+ request.Version = 1;
+ request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
+ request.QueryType = requestType;
+ request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
+
+ if (protocol == QDnsLookup::Standard && !nameserver.isNull()) {
+ memset(dnsAddresses, 0, sizeof(dnsAddresses));
+ request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
+ auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
+ auto sa = new (addr[0].MaxSa) sockaddr;
+ request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
+ request.pDnsServerList->AddrCount = 1;
+ // ### setting port 53 seems to cause some systems to fail
+ setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
+ request.pDnsServerList->Family = sa->sa_family;
+ }
+
+ DNS_QUERY_RESULT results = {};
+ results.Version = 1;
+ DNS_STATUS status = ERROR_INVALID_PARAMETER;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ status = DnsQueryEx(&request, &results, nullptr);
+ break;
+ case QDnsLookup::DnsOverTls:
+ status = sendAlternate(this, reply, &request, &results);
break;
- case DNS_ERROR_RCODE_FORMAT_ERROR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
- return;
- case DNS_ERROR_RCODE_SERVER_FAILURE:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
- return;
- case DNS_ERROR_RCODE_NAME_ERROR:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case DNS_ERROR_RCODE_REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = QSystemError(status, QSystemError::NativeError).toString();
- return;
}
+ if (status == DNS_STATUS(-1))
+ return; // error already set in reply
+ if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
+ return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
+ else if (status == ERROR_TIMEOUT)
+ return reply->makeTimeoutError();
+ else if (status != ERROR_SUCCESS)
+ return reply->makeResolverSystemError(status);
+
+ QStringView lastEncodedName;
+ QString cachedDecodedName;
+ auto extractAndCacheHost = [&](QStringView name) -> const QString & {
+ lastEncodedName = name;
+ cachedDecodedName = decodeLabel(name);
+ return cachedDecodedName;
+ };
+ auto extractMaybeCachedHost = [&](QStringView name) -> const QString & {
+ return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name);
+ };
+
// Extract results.
- for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) {
- const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() );
+ for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
+ // warning: always assign name to the record before calling extractXxxHost() again
+ const QString &name = extractMaybeCachedHost(ptr->pName);
if (ptr->wType == QDnsLookup::A) {
QDnsHostAddressRecord record;
record.d->name = name;
@@ -119,12 +194,12 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1());
+ record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost);
reply->canonicalNameRecords.append(record);
} else if (ptr->wType == QDnsLookup::MX) {
QDnsMailExchangeRecord record;
record.d->name = name;
- record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1());
+ record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
record.d->preference = ptr->Data.Mx.wPreference;
record.d->timeToLive = ptr->dwTtl;
reply->mailExchangeRecords.append(record);
@@ -132,35 +207,54 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
reply->nameServerRecords.append(record);
} else if (ptr->wType == QDnsLookup::PTR) {
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
reply->pointerRecords.append(record);
} else if (ptr->wType == QDnsLookup::SRV) {
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1());
+ record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
record.d->port = ptr->Data.Srv.wPort;
record.d->priority = ptr->Data.Srv.wPriority;
record.d->timeToLive = ptr->dwTtl;
record.d->weight = ptr->Data.Srv.wWeight;
reply->serviceRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::TLSA) {
+ // Note: untested, because the DNS_RECORD reply appears to contain
+ // no records relating to TLSA. Maybe WinDNS filters them out of
+ // zones without DNSSEC.
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+
+ const auto &tlsa = ptr->Data.Tlsa;
+ const quint8 usage = tlsa.bCertUsage;
+ const quint8 selector = tlsa.bSelector;
+ const quint8 matchType = tlsa.bMatchingType;
+
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(tlsa.bCertificateAssociationData,
+ tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (ptr->wType == QDnsLookup::TXT) {
QDnsTextRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
- record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();;
+ record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
}
reply->textRecords.append(record);
}
}
- DnsRecordListFree(dns_records, DnsFreeRecordList);
+ DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList);
}
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_winrt.cpp b/src/network/kernel/qdnslookup_winrt.cpp
deleted file mode 100644
index 30510d89fc..0000000000
--- a/src/network/kernel/qdnslookup_winrt.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qdnslookup_p.h"
-
-#include <qfunctions_winrt.h>
-#include <qurl.h>
-#include <qdebug.h>
-
-#include <wrl.h>
-#include <windows.foundation.h>
-#include <windows.foundation.collections.h>
-#include <windows.networking.h>
-#include <windows.networking.sockets.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Foundation::Collections;
-using namespace ABI::Windows::Networking;
-using namespace ABI::Windows::Networking::Connectivity;
-using namespace ABI::Windows::Networking::Sockets;
-
-#define E_NO_SUCH_HOST 0x80072af9
-
-QT_BEGIN_NAMESPACE
-
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- // TODO: Add nameserver support for winRT
- if (!nameserver.isNull())
- qWarning("Ignoring nameserver as its currently not supported on WinRT");
-
- // TODO: is there any way to do "proper" dns lookup?
- if (requestType != QDnsLookup::A && requestType != QDnsLookup::AAAA
- && requestType != QDnsLookup::ANY) {
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = QLatin1String("WinRT only supports IPv4 and IPv6 requests");
- return;
- }
-
- QString aceHostname = QUrl::fromAce(requestName);
- if (aceHostname.isEmpty()) {
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = requestName.isEmpty() ? tr("No hostname given") : tr("Invalid hostname");
- return;
- }
-
- ComPtr<IHostNameFactory> hostnameFactory;
- HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- IID_PPV_ARGS(&hostnameFactory));
- if (FAILED(hr)) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = QLatin1String("Could not obtain hostname factory");
- return;
- }
- ComPtr<IHostName> host;
- HStringReference hostNameRef((const wchar_t*)aceHostname.utf16());
- hr = hostnameFactory->CreateHostName(hostNameRef.Get(), &host);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IDatagramSocketStatics> datagramSocketStatics;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &datagramSocketStatics);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IAsyncOperation<IVectorView<EndpointPair *> *>> op;
- hr = datagramSocketStatics->GetEndpointPairsAsync(host.Get(),
- HString::MakeReference(L"0").Get(),
- &op);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IVectorView<EndpointPair *>> endpointPairs;
- hr = QWinRTFunctions::await(op, endpointPairs.GetAddressOf(), QWinRTFunctions::YieldThread, 60 * 1000);
- if (hr == E_NO_SUCH_HOST || !endpointPairs) {
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Host %1 could not be found.").arg(aceHostname);
- return;
- }
- if (FAILED(hr)) {
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Unknown error");
- return;
- }
-
- unsigned int size;
- hr = endpointPairs->get_Size(&size);
- Q_ASSERT_SUCCEEDED(hr);
- // endpoint pairs might contain duplicates so we temporarily store addresses in a QSet
- QSet<QHostAddress> addresses;
- for (unsigned int i = 0; i < size; ++i) {
- ComPtr<IEndpointPair> endpointpair;
- hr = endpointPairs->GetAt(i, &endpointpair);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IHostName> remoteHost;
- hr = endpointpair->get_RemoteHostName(&remoteHost);
- Q_ASSERT_SUCCEEDED(hr);
- HostNameType type;
- hr = remoteHost->get_Type(&type);
- Q_ASSERT_SUCCEEDED(hr);
- if (type == HostNameType_Bluetooth || type == HostNameType_DomainName
- || (requestType != QDnsLookup::ANY
- && ((type == HostNameType_Ipv4 && requestType == QDnsLookup::AAAA)
- || (type == HostNameType_Ipv6 && requestType == QDnsLookup::A))))
- continue;
-
- HString name;
- hr = remoteHost->get_CanonicalName(name.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 length;
- PCWSTR rawString = name.GetRawBuffer(&length);
- addresses.insert(QHostAddress(QString::fromWCharArray(rawString, length)));
- }
- for (const QHostAddress &address : qAsConst(addresses)) {
- QDnsHostAddressRecord record;
- record.d->name = aceHostname;
- record.d->value = address;
- reply->hostAddressRecords.append(record);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index ed1c23ed6e..0330fb091b 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhostaddress.h"
#include "qhostaddress_p.h"
@@ -65,7 +29,7 @@
QT_BEGIN_NAMESPACE
QHostAddressPrivate::QHostAddressPrivate()
- : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol)
+ : a(0), protocol(QHostAddress::UnknownNetworkLayerProtocol)
{
memset(&a6, 0, sizeof(a6));
}
@@ -73,7 +37,7 @@ QHostAddressPrivate::QHostAddressPrivate()
void QHostAddressPrivate::setAddress(quint32 a_)
{
a = a_;
- protocol = QAbstractSocket::IPv4Protocol;
+ protocol = QHostAddress::IPv4Protocol;
//create mapped address, except for a_ == 0 (any)
a6_64.c[0] = 0;
@@ -122,7 +86,7 @@ static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6, const QHostAddress::
void QHostAddressPrivate::setAddress(const quint8 *a_)
{
- protocol = QAbstractSocket::IPv6Protocol;
+ protocol = QHostAddress::IPv6Protocol;
memcpy(a6.c, a_, sizeof(a6));
a = 0;
convertToIpv4(a, a6, (QHostAddress::ConvertV4MappedToIPv4
@@ -136,26 +100,26 @@ void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr, QString *scopeId)
{
- QStringRef tmp(&address);
- int scopeIdPos = tmp.lastIndexOf(QLatin1Char('%'));
+ QStringView tmp(address);
+ qsizetype scopeIdPos = tmp.lastIndexOf(u'%');
if (scopeIdPos != -1) {
*scopeId = tmp.mid(scopeIdPos + 1).toString();
tmp.chop(tmp.size() - scopeIdPos);
} else {
scopeId->clear();
}
- return QIPAddressUtils::parseIp6(addr, tmp.constBegin(), tmp.constEnd()) == nullptr;
+ return QIPAddressUtils::parseIp6(addr, tmp.begin(), tmp.end()) == nullptr;
}
bool QHostAddressPrivate::parse(const QString &ipString)
{
- protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ protocol = QHostAddress::UnknownNetworkLayerProtocol;
QString a = ipString.simplified();
if (a.isEmpty())
return false;
// All IPv6 addresses contain a ':', and may contain a '.'.
- if (a.contains(QLatin1Char(':'))) {
+ if (a.contains(u':')) {
quint8 maybeIp6[16];
if (parseIp6(a, maybeIp6, &scopeId)) {
setAddress(maybeIp6);
@@ -175,7 +139,7 @@ bool QHostAddressPrivate::parse(const QString &ipString)
void QHostAddressPrivate::clear()
{
a = 0;
- protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ protocol = QHostAddress::UnknownNetworkLayerProtocol;
memset(&a6, 0, sizeof(a6));
}
@@ -200,8 +164,12 @@ AddressClassification QHostAddressPrivate::classify() const
return BroadcastAddress;
return UnknownAddress;
}
+ if (((a & 0xff000000U) == 0x0a000000U) // 10.0.0.0/8
+ || ((a & 0xfff00000U) == 0xac100000U) // 172.16.0.0/12
+ || ((a & 0xffff0000U) == 0xc0a80000U)) // 192.168.0.0/16
+ return PrivateNetworkAddress;
- // Not testing for PrivateNetworkAddress and TestNetworkAddress
+ // Not testing for TestNetworkAddress
// since we don't need them yet.
return GlobalAddress;
}
@@ -242,7 +210,7 @@ AddressClassification QHostAddressPrivate::classify() const
if (low64) // not ::
return GlobalAddress;
- if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol)
+ if (protocol == QHostAddress::UnknownNetworkLayerProtocol)
return UnknownAddress;
// only :: and 0.0.0.0 remain now
@@ -262,10 +230,10 @@ bool QNetmask::setAddress(const QHostAddress &address)
quint8 *end;
length = 255;
- if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ if (address.protocol() == QHostAddress::IPv4Protocol) {
ip.v4 = qToBigEndian(address.toIPv4Address());
end = ptr + 4;
- } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ } else if (address.protocol() == QHostAddress::IPv6Protocol) {
memcpy(ip.v6, address.toIPv6Address().c, 16);
end = ptr + 16;
} else {
@@ -331,12 +299,12 @@ static void clearBits(quint8 *where, int start, int end)
memset(where + (start + 7) / 8, 0, end / 8 - (start + 7) / 8);
}
-QHostAddress QNetmask::address(QAbstractSocket::NetworkLayerProtocol protocol) const
+QHostAddress QNetmask::address(QHostAddress::NetworkLayerProtocol protocol) const
{
- if (length == 255 || protocol == QAbstractSocket::AnyIPProtocol ||
- protocol == QAbstractSocket::UnknownNetworkLayerProtocol) {
+ if (length == 255 || protocol == QHostAddress::AnyIPProtocol ||
+ protocol == QHostAddress::UnknownNetworkLayerProtocol) {
return QHostAddress();
- } else if (protocol == QAbstractSocket::IPv4Protocol) {
+ } else if (protocol == QHostAddress::IPv4Protocol) {
quint32 a;
if (length == 0)
a = 0;
@@ -423,18 +391,6 @@ QHostAddress::QHostAddress(quint32 ip4Addr)
}
/*!
- Constructs a host address object with the IPv6 address \a ip6Addr.
-
- \a ip6Addr must be a 16-byte array in network byte order (big
- endian).
-*/
-QHostAddress::QHostAddress(quint8 *ip6Addr)
- : d(new QHostAddressPrivate)
-{
- setAddress(ip6Addr);
-}
-
-/*!
\since 5.5
Constructs a host address object with the IPv6 address \a ip6Addr.
@@ -479,14 +435,10 @@ QHostAddress::QHostAddress(const QString &address)
QHostAddress::QHostAddress(const struct sockaddr *sockaddr)
: d(new QHostAddressPrivate)
{
-#ifndef Q_OS_WINRT
if (sockaddr->sa_family == AF_INET)
setAddress(htonl(((const sockaddr_in *)sockaddr)->sin_addr.s_addr));
else if (sockaddr->sa_family == AF_INET6)
setAddress(((const sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr);
-#else
- Q_UNUSED(sockaddr)
-#endif
}
/*!
@@ -523,20 +475,6 @@ QHostAddress &QHostAddress::operator=(const QHostAddress &address)
return *this;
}
-#if QT_DEPRECATED_SINCE(5, 8)
-/*!
- Assigns the host address \a address to this object, and returns a
- reference to this object.
-
- \sa setAddress()
-*/
-QHostAddress &QHostAddress::operator=(const QString &address)
-{
- setAddress(address);
- return *this;
-}
-#endif
-
/*!
\since 5.8
Assigns the special address \a address to this object, and returns a
@@ -596,20 +534,6 @@ void QHostAddress::setAddress(quint32 ip4Addr)
/*!
\overload
-
- Set the IPv6 address specified by \a ip6Addr.
-
- \a ip6Addr must be an array of 16 bytes in network byte order
- (high-order byte first).
-*/
-void QHostAddress::setAddress(quint8 *ip6Addr)
-{
- d.detach();
- d->setAddress(ip6Addr);
-}
-
-/*!
- \overload
\since 5.5
Set the IPv6 address specified by \a ip6Addr.
@@ -659,15 +583,11 @@ bool QHostAddress::setAddress(const QString &address)
void QHostAddress::setAddress(const struct sockaddr *sockaddr)
{
d.detach();
-#ifndef Q_OS_WINRT
clear();
if (sockaddr->sa_family == AF_INET)
setAddress(htonl(((const sockaddr_in *)sockaddr)->sin_addr.s_addr));
else if (sockaddr->sa_family == AF_INET6)
setAddress(((const sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr);
-#else
- Q_UNUSED(sockaddr)
-#endif
}
/*!
@@ -705,7 +625,7 @@ void QHostAddress::setAddress(SpecialAddress address)
return;
case Any:
- d->protocol = QAbstractSocket::AnyIPProtocol;
+ d->protocol = QHostAddress::AnyIPProtocol;
return;
}
@@ -723,26 +643,7 @@ void QHostAddress::setAddress(SpecialAddress address)
\l{QAbstractSocket::}{IPv4Protocol},
or if the protocol is
\l{QAbstractSocket::}{IPv6Protocol},
- and the IPv6 address is an IPv4 mapped address. (RFC4291)
-
- \sa toString()
-*/
-quint32 QHostAddress::toIPv4Address() const
-{
- return toIPv4Address(nullptr);
-}
-
-/*!
- Returns the IPv4 address as a number.
-
- For example, if the address is 127.0.0.1, the returned value is
- 2130706433 (i.e. 0x7f000001).
-
- This value is valid if the protocol() is
- \l{QAbstractSocket::}{IPv4Protocol},
- or if the protocol is
- \l{QAbstractSocket::}{IPv6Protocol},
- and the IPv6 address is an IPv4 mapped address. (RFC4291). In those
+ and the IPv6 address is an IPv4 mapped address (RFC4291). In those
cases, \a ok will be set to true. Otherwise, it will be set to false.
\sa toString()
@@ -751,8 +652,8 @@ quint32 QHostAddress::toIPv4Address(bool *ok) const
{
quint32 dummy;
if (ok)
- *ok = d->protocol == QAbstractSocket::IPv4Protocol || d->protocol == QAbstractSocket::AnyIPProtocol
- || (d->protocol == QAbstractSocket::IPv6Protocol
+ *ok = d->protocol == QHostAddress::IPv4Protocol || d->protocol == QHostAddress::AnyIPProtocol
+ || (d->protocol == QHostAddress::IPv6Protocol
&& convertToIpv4(dummy, d->a6, ConversionMode(QHostAddress::ConvertV4MappedToIPv4
| QHostAddress::ConvertUnspecifiedAddress)));
return d->a;
@@ -761,9 +662,9 @@ quint32 QHostAddress::toIPv4Address(bool *ok) const
/*!
Returns the network layer protocol of the host address.
*/
-QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
+QHostAddress::NetworkLayerProtocol QHostAddress::protocol() const
{
- return QAbstractSocket::NetworkLayerProtocol(d->protocol);
+ return QHostAddress::NetworkLayerProtocol(d->protocol);
}
/*!
@@ -776,7 +677,7 @@ QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
\l{QAbstractSocket::}{IPv6Protocol}.
If the protocol is
\l{QAbstractSocket::}{IPv4Protocol},
- then the address is returned an an IPv4 mapped IPv6 address. (RFC4291)
+ then the address is returned as an IPv4 mapped IPv6 address. (RFC4291)
\sa toString()
*/
@@ -798,14 +699,14 @@ Q_IPV6ADDR QHostAddress::toIPv6Address() const
QString QHostAddress::toString() const
{
QString s;
- if (d->protocol == QAbstractSocket::IPv4Protocol
- || d->protocol == QAbstractSocket::AnyIPProtocol) {
+ if (d->protocol == QHostAddress::IPv4Protocol
+ || d->protocol == QHostAddress::AnyIPProtocol) {
quint32 i = toIPv4Address();
QIPAddressUtils::toString(s, i);
- } else if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ } else if (d->protocol == QHostAddress::IPv6Protocol) {
QIPAddressUtils::toString(s, d->a6.c);
if (!d->scopeId.isEmpty())
- s.append(QLatin1Char('%') + d->scopeId);
+ s += u'%' + d->scopeId;
}
return s;
}
@@ -848,7 +749,7 @@ QString QHostAddress::toString() const
*/
QString QHostAddress::scopeId() const
{
- return (d->protocol == QAbstractSocket::IPv6Protocol) ? d->scopeId : QString();
+ return (d->protocol == QHostAddress::IPv6Protocol) ? d->scopeId : QString();
}
/*!
@@ -866,7 +767,7 @@ QString QHostAddress::scopeId() const
void QHostAddress::setScopeId(const QString &id)
{
d.detach();
- if (d->protocol == QAbstractSocket::IPv6Protocol)
+ if (d->protocol == QHostAddress::IPv6Protocol)
d->scopeId = id;
}
@@ -887,7 +788,7 @@ bool QHostAddress::operator==(const QHostAddress &other) const
Returns \c true if this host address is the same as the \a other address
given; otherwise returns \c false.
- The parameter \a mode controls which conversions are preformed between addresses
+ The parameter \a mode controls which conversions are performed between addresses
of differing protocols. If no \a mode is given, \c TolerantConversion is performed
by default.
@@ -898,41 +799,41 @@ bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
if (d == other.d)
return true;
- if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ if (d->protocol == QHostAddress::IPv4Protocol) {
switch (other.d->protocol) {
- case QAbstractSocket::IPv4Protocol:
+ case QHostAddress::IPv4Protocol:
return d->a == other.d->a;
- case QAbstractSocket::IPv6Protocol:
+ case QHostAddress::IPv6Protocol:
quint32 a4;
return convertToIpv4(a4, other.d->a6, mode) && (a4 == d->a);
- case QAbstractSocket::AnyIPProtocol:
+ case QHostAddress::AnyIPProtocol:
return (mode & QHostAddress::ConvertUnspecifiedAddress) && d->a == 0;
- case QAbstractSocket::UnknownNetworkLayerProtocol:
+ case QHostAddress::UnknownNetworkLayerProtocol:
return false;
}
}
- if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ if (d->protocol == QHostAddress::IPv6Protocol) {
switch (other.d->protocol) {
- case QAbstractSocket::IPv4Protocol:
+ case QHostAddress::IPv4Protocol:
quint32 a4;
return convertToIpv4(a4, d->a6, mode) && (a4 == other.d->a);
- case QAbstractSocket::IPv6Protocol:
+ case QHostAddress::IPv6Protocol:
return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
- case QAbstractSocket::AnyIPProtocol:
+ case QHostAddress::AnyIPProtocol:
return (mode & QHostAddress::ConvertUnspecifiedAddress)
- && (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
- case QAbstractSocket::UnknownNetworkLayerProtocol:
+ && (d->a6_64.c[0] == 0) && (d->a6_64.c[1] == 0);
+ case QHostAddress::UnknownNetworkLayerProtocol:
return false;
}
}
- if ((d->protocol == QAbstractSocket::AnyIPProtocol)
+ if ((d->protocol == QHostAddress::AnyIPProtocol)
&& (mode & QHostAddress::ConvertUnspecifiedAddress)) {
switch (other.d->protocol) {
- case QAbstractSocket::IPv4Protocol:
+ case QHostAddress::IPv4Protocol:
return other.d->a == 0;
- case QAbstractSocket::IPv6Protocol:
+ case QHostAddress::IPv6Protocol:
return (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
default:
break;
@@ -951,7 +852,7 @@ bool QHostAddress::operator ==(SpecialAddress other) const
quint32 ip4 = INADDR_ANY;
switch (other) {
case Null:
- return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol;
+ return d->protocol == QHostAddress::UnknownNetworkLayerProtocol;
case Broadcast:
ip4 = INADDR_BROADCAST;
@@ -962,14 +863,14 @@ bool QHostAddress::operator ==(SpecialAddress other) const
break;
case Any:
- return d->protocol == QAbstractSocket::AnyIPProtocol;
+ return d->protocol == QHostAddress::AnyIPProtocol;
case AnyIPv4:
break;
case LocalHostIPv6:
case AnyIPv6:
- if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ if (d->protocol == QHostAddress::IPv6Protocol) {
quint64 second = quint8(other == LocalHostIPv6); // 1 for localhost, 0 for any
return d->a6_64.c[0] == 0 && d->a6_64.c[1] == qToBigEndian(second);
}
@@ -977,7 +878,7 @@ bool QHostAddress::operator ==(SpecialAddress other) const
}
// common IPv4 part
- return d->protocol == QAbstractSocket::IPv4Protocol && d->a == ip4;
+ return d->protocol == QHostAddress::IPv4Protocol && d->a == ip4;
}
/*!
@@ -989,7 +890,7 @@ bool QHostAddress::operator ==(SpecialAddress other) const
*/
bool QHostAddress::isNull() const
{
- return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol;
+ return d->protocol == QHostAddress::UnknownNetworkLayerProtocol;
}
/*!
@@ -1022,14 +923,14 @@ bool QHostAddress::isInSubnet(const QHostAddress &subnet, int netmask) const
} ip4, net4;
const quint8 *ip;
const quint8 *net;
- if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ if (d->protocol == QHostAddress::IPv4Protocol) {
if (netmask > 32)
netmask = 32;
ip4.ip = qToBigEndian(d->a);
net4.ip = qToBigEndian(subnet.d->a);
ip = ip4.data;
net = net4.data;
- } else if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ } else if (d->protocol == QHostAddress::IPv6Protocol) {
if (netmask > 128)
netmask = 128;
ip = d->a6.c;
@@ -1110,17 +1011,17 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
if (subnet.isEmpty())
return invalid;
- int slash = subnet.indexOf(QLatin1Char('/'));
- QStringRef netStr(&subnet);
+ qsizetype slash = subnet.indexOf(u'/');
+ QStringView netStr(subnet);
if (slash != -1)
netStr.truncate(slash);
int netmask = -1;
- bool isIpv6 = netStr.contains(QLatin1Char(':'));
+ bool isIpv6 = netStr.contains(u':');
if (slash != -1) {
// is the netmask given in IP-form or in bit-count form?
- if (!isIpv6 && subnet.indexOf(QLatin1Char('.'), slash + 1) != -1) {
+ if (!isIpv6 && subnet.indexOf(u'.', slash + 1) != -1) {
// IP-style, convert it to bit-count form
QHostAddress mask;
QNetmask parser;
@@ -1131,7 +1032,7 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
netmask = parser.prefixLength();
} else {
bool ok;
- netmask = subnet.midRef(slash + 1).toUInt(&ok);
+ netmask = QStringView{subnet}.mid(slash + 1).toUInt(&ok);
if (!ok)
return invalid; // failed to parse the subnet
}
@@ -1156,15 +1057,15 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
return invalid; // invalid netmask
// parse the address manually
- auto parts = netStr.split(QLatin1Char('.'));
- if (parts.isEmpty() || parts.count() > 4)
+ auto parts = netStr.split(u'.');
+ if (parts.isEmpty() || parts.size() > 4)
return invalid; // invalid IPv4 address
if (parts.constLast().isEmpty())
parts.removeLast();
quint32 addr = 0;
- for (int i = 0; i < parts.count(); ++i) {
+ for (int i = 0; i < parts.size(); ++i) {
bool ok;
uint byteValue = parts.at(i).toUInt(&ok);
if (!ok || byteValue > 255)
@@ -1173,9 +1074,9 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
addr <<= 8;
addr += byteValue;
}
- addr <<= 8 * (4 - parts.count());
+ addr <<= 8 * (4 - parts.size());
if (netmask == -1) {
- netmask = 8 * parts.count();
+ netmask = 8 * parts.size();
} else if (netmask == 0) {
// special case here
// x86's instructions "shr" and "shl" do not operate when
@@ -1210,13 +1111,13 @@ bool QHostAddress::isLoopback() const
Note that IPv6 unique local unicast addresses are considered global
addresses (see isUniqueLocalUnicast()), as are IPv4 addresses reserved for
- local networks by \l {https://tools.ietf.org/html/rfc1918}{RFC 1918}.
+ local networks by \l {RFC 1918}.
Also note that IPv6 site-local addresses are deprecated and should be
considered as global in new applications. This function returns true for
site-local addresses too.
- \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isGlobal() const
{
@@ -1234,7 +1135,7 @@ bool QHostAddress::isGlobal() const
\l{https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml}{IANA
IPv6 Address Space} registry for more information.
- \sa isLoopback(), isGlobal(), isMulticast(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isLinkLocal() const
{
@@ -1257,7 +1158,7 @@ bool QHostAddress::isLinkLocal() const
isGlobal() also returns true). Site-local addresses were replaced by Unique
Local Addresses (ULA).
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isSiteLocal() const
{
@@ -1278,7 +1179,7 @@ bool QHostAddress::isSiteLocal() const
4193 says that, in practice, "applications may treat these addresses like
global scoped addresses." Only routers need care about the distinction.
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isUniqueLocalUnicast() const
{
@@ -1291,7 +1192,7 @@ bool QHostAddress::isUniqueLocalUnicast() const
Returns \c true if the address is an IPv4 or IPv6 multicast address, \c
false otherwise.
- \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isMulticast() const
{
@@ -1308,13 +1209,27 @@ bool QHostAddress::isMulticast() const
broadcast address. For that, please use \l QNetworkInterface to obtain the
broadcast addresses of the local machine.
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isBroadcast() const
{
return d->classify() == BroadcastAddress;
}
+/*!
+ \since 6.6
+
+ Returns \c true if the address is an IPv6 unique local unicast address or
+ IPv4 address reserved for local networks by \l {RFC 1918}, \c false otherwise.
+
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isBroadcast()
+*/
+bool QHostAddress::isPrivateUse() const
+{
+ const AddressClassification classification = d->classify();
+ return (classification == PrivateNetworkAddress) || (classification == UniqueLocalAddress);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const QHostAddress &address)
{
@@ -1333,14 +1248,13 @@ QDebug operator<<(QDebug d, const QHostAddress &address)
\relates QHostAddress
Returns a hash of the host address \a key, using \a seed to seed the calculation.
*/
-uint qHash(const QHostAddress &key, uint seed) noexcept
+size_t qHash(const QHostAddress &key, size_t seed) noexcept
{
return qHashBits(key.d->a6.c, 16, seed);
}
/*!
- \fn bool operator==(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
- \relates QHostAddress
+ \fn bool QHostAddress::operator==(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
Returns \c true if special address \a lhs is the same as host address \a rhs;
otherwise returns \c false.
@@ -1349,8 +1263,7 @@ uint qHash(const QHostAddress &key, uint seed) noexcept
*/
/*!
- \fn bool operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
- \relates QHostAddress
+ \fn bool QHostAddress::operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
\since 5.9
Returns \c false if special address \a lhs is the same as host address \a rhs;
@@ -1374,13 +1287,13 @@ QDataStream &operator<<(QDataStream &out, const QHostAddress &address)
prot = qint8(address.protocol());
out << prot;
switch (address.protocol()) {
- case QAbstractSocket::UnknownNetworkLayerProtocol:
- case QAbstractSocket::AnyIPProtocol:
+ case QHostAddress::UnknownNetworkLayerProtocol:
+ case QHostAddress::AnyIPProtocol:
break;
- case QAbstractSocket::IPv4Protocol:
+ case QHostAddress::IPv4Protocol:
out << address.toIPv4Address();
break;
- case QAbstractSocket::IPv6Protocol:
+ case QHostAddress::IPv6Protocol:
{
Q_IPV6ADDR ipv6 = address.toIPv6Address();
for (int i = 0; i < 16; ++i)
@@ -1403,18 +1316,18 @@ QDataStream &operator>>(QDataStream &in, QHostAddress &address)
{
qint8 prot;
in >> prot;
- switch (QAbstractSocket::NetworkLayerProtocol(prot)) {
- case QAbstractSocket::UnknownNetworkLayerProtocol:
+ switch (QHostAddress::NetworkLayerProtocol(prot)) {
+ case QHostAddress::UnknownNetworkLayerProtocol:
address.clear();
break;
- case QAbstractSocket::IPv4Protocol:
+ case QHostAddress::IPv4Protocol:
{
quint32 ipv4;
in >> ipv4;
address.setAddress(ipv4);
}
break;
- case QAbstractSocket::IPv6Protocol:
+ case QHostAddress::IPv6Protocol:
{
Q_IPV6ADDR ipv6;
for (int i = 0; i < 16; ++i)
@@ -1426,7 +1339,7 @@ QDataStream &operator>>(QDataStream &in, QHostAddress &address)
address.setScopeId(scope);
}
break;
- case QAbstractSocket::AnyIPProtocol:
+ case QHostAddress::AnyIPProtocol:
address = QHostAddress::Any;
break;
default:
@@ -1439,3 +1352,5 @@ QDataStream &operator>>(QDataStream &in, QHostAddress &address)
#endif //QT_NO_DATASTREAM
QT_END_NAMESPACE
+
+#include "moc_qhostaddress.cpp"
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 799247695e..6aa045c959 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHOSTADDRESS_H
#define QHOSTADDRESS_H
@@ -45,7 +9,9 @@
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
#include <QtCore/qshareddata.h>
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
#include <QtNetwork/qabstractsocket.h>
+#endif
struct sockaddr;
@@ -54,7 +20,7 @@ QT_BEGIN_NAMESPACE
class QHostAddressPrivate;
-class Q_NETWORK_EXPORT QIPv6Address
+class QT6_ONLY(Q_NETWORK_EXPORT) QIPv6Address
{
public:
inline quint8 &operator [](int index) { return c[index]; }
@@ -66,10 +32,11 @@ typedef QIPv6Address Q_IPV6ADDR;
class QHostAddress;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QHostAddress &key, size_t seed = 0) noexcept;
class Q_NETWORK_EXPORT QHostAddress
{
+ Q_GADGET
public:
enum SpecialAddress {
Null,
@@ -91,9 +58,24 @@ public:
};
Q_DECLARE_FLAGS(ConversionMode, ConversionModeFlag)
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ using NetworkLayerProtocol = QAbstractSocket::NetworkLayerProtocol;
+ static constexpr auto IPv4Protocol = QAbstractSocket::IPv4Protocol;
+ static constexpr auto IPv6Protocol = QAbstractSocket::IPv6Protocol;
+ static constexpr auto AnyIPProtocol = QAbstractSocket::AnyIPProtocol;
+ static constexpr auto UnknownNetworkLayerProtocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+#else
+ enum NetworkLayerProtocol {
+ IPv4Protocol,
+ IPv6Protocol,
+ AnyIPProtocol,
+ UnknownNetworkLayerProtocol = -1
+ };
+ Q_ENUM(NetworkLayerProtocol)
+#endif
+
QHostAddress();
explicit QHostAddress(quint32 ip4Addr);
- explicit QHostAddress(quint8 *ip6Addr); // ### Qt 6: remove me
explicit QHostAddress(const quint8 *ip6Addr);
explicit QHostAddress(const Q_IPV6ADDR &ip6Addr);
explicit QHostAddress(const sockaddr *address);
@@ -105,25 +87,19 @@ public:
QHostAddress &operator=(QHostAddress &&other) noexcept
{ swap(other); return *this; }
QHostAddress &operator=(const QHostAddress &other);
-#if QT_DEPRECATED_SINCE(5, 8)
- QT_DEPRECATED_X("use = QHostAddress(string) instead")
- QHostAddress &operator=(const QString &address);
-#endif
QHostAddress &operator=(SpecialAddress address);
void swap(QHostAddress &other) noexcept { d.swap(other.d); }
void setAddress(quint32 ip4Addr);
- void setAddress(quint8 *ip6Addr); // ### Qt 6: remove me
void setAddress(const quint8 *ip6Addr);
void setAddress(const Q_IPV6ADDR &ip6Addr);
void setAddress(const sockaddr *address);
bool setAddress(const QString &address);
void setAddress(SpecialAddress address);
- QAbstractSocket::NetworkLayerProtocol protocol() const;
- quint32 toIPv4Address() const; // ### Qt6: merge with next overload
- quint32 toIPv4Address(bool *ok) const;
+ NetworkLayerProtocol protocol() const;
+ quint32 toIPv4Address(bool *ok = nullptr) const;
Q_IPV6ADDR toIPv6Address() const;
QString toString() const;
@@ -151,21 +127,23 @@ public:
bool isUniqueLocalUnicast() const;
bool isMulticast() const;
bool isBroadcast() const;
+ bool isPrivateUse() const;
static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
- friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QHostAddress &key, size_t seed) noexcept;
+
+ friend bool operator ==(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
+ { return rhs == lhs; }
+ friend bool operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
+ { return rhs != lhs; }
+
protected:
friend class QHostAddressPrivate;
QExplicitlySharedDataPointer<QHostAddressPrivate> d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QHostAddress::ConversionMode)
-Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QHostAddress)
-
-inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2)
-{ return address2 == address1; }
-inline bool operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
-{ return rhs != lhs; }
+Q_DECLARE_SHARED(QHostAddress)
#ifndef QT_NO_DEBUG_STREAM
Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QHostAddress &);
diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h
index 4dc2989011..98586fb374 100644
--- a/src/network/kernel/qhostaddress_p.h
+++ b/src/network/kernel/qhostaddress_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHOSTADDRESSPRIVATE_H
#define QHOSTADDRESSPRIVATE_H
@@ -78,7 +42,7 @@ class QNetmask
// stores 0-32 for IPv4, 0-128 for IPv6, or 255 for invalid
quint8 length;
public:
- Q_DECL_CONSTEXPR QNetmask() : length(255) {}
+ constexpr QNetmask() : length(255) {}
bool setAddress(const QHostAddress &address);
QHostAddress address(QAbstractSocket::NetworkLayerProtocol protocol) const;
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 31671feece..62bb210ca1 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QHOSTINFO_DEBUG
@@ -43,6 +7,7 @@
#include "qhostinfo_p.h"
#include <qplatformdefs.h>
+#include "QtCore/qapplicationstatic.h"
#include "QtCore/qscopedpointer.h"
#include <qabstracteventdispatcher.h>
#include <qcoreapplication.h>
@@ -51,7 +16,6 @@
#include <qstringlist.h>
#include <qthread.h>
#include <qurl.h>
-#include <private/qnetworksession_p.h>
#include <algorithm>
@@ -59,7 +23,7 @@
# include <unistd.h>
# include <netdb.h>
# include <netinet/in.h>
-# if defined(AI_ADDRCONFIG)
+# if defined(AI_ADDRCONFIG) && !defined(Q_OS_WASM)
# define Q_ADDRCONFIG AI_ADDRCONFIG
# endif
#elif defined Q_OS_WIN
@@ -70,9 +34,11 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
//#define QHOSTINFO_DEBUG
-Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
+QT_IMPL_METATYPE_EXTERN(QHostInfo)
namespace {
struct ToBeLookedUpEquals {
@@ -102,8 +68,20 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI
return std::make_pair(dest1, dest2);
}
+Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
+
+}
+
+QHostInfoResult::QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot)
+ : receiver{receiver ? receiver : this}, slotObj{std::move(slot)}
+{
+ Q_ASSERT(this->receiver);
+ moveToThread(this->receiver->thread());
}
+QHostInfoResult::~QHostInfoResult()
+ = default;
+
/*
The calling thread is likely the one that executes the lookup via
QHostInfoRunnable. Unless we operate with a queued connection already,
@@ -112,8 +90,8 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI
the thread that made the call to lookupHost. That QHostInfoResult object
then calls the user code in the correct thread.
- The 'result' object deletes itself (via deleteLater) when the metacall
- event is received.
+ The 'result' object deletes itself (via deleteLater) when
+ finalizePostResultsReady is called.
*/
void QHostInfoResult::postResultsReady(const QHostInfo &info)
{
@@ -123,53 +101,33 @@ void QHostInfoResult::postResultsReady(const QHostInfo &info)
return;
}
// we used to have a context object, but it's already destroyed
- if (withContextObject && !receiver)
+ if (!receiver)
return;
- static const int signal_index = []() -> int {
- auto senderMetaObject = &QHostInfoResult::staticMetaObject;
- auto signal = &QHostInfoResult::resultsReady;
- int signal_index = -1;
- void *args[] = { &signal_index, &signal };
- senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
- return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject);
- }();
-
// a long-living version of this
auto result = new QHostInfoResult(this);
Q_CHECK_PTR(result);
- const int nargs = 2;
- auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs);
- Q_CHECK_PTR(metaCallEvent);
- void **args = metaCallEvent->args();
- int *types = metaCallEvent->types();
- types[0] = QMetaType::type("void");
- types[1] = QMetaType::type("QHostInfo");
- args[0] = nullptr;
- args[1] = QMetaType::create(types[1], &info);
- Q_CHECK_PTR(args[1]);
- qApp->postEvent(result, metaCallEvent);
+ QMetaObject::invokeMethod(result,
+ &QHostInfoResult::finalizePostResultsReady,
+ Qt::QueuedConnection,
+ info);
}
/*
- Receives the event posted by postResultsReady, and calls the functor.
+ Receives the info from postResultsReady, and calls the functor.
*/
-bool QHostInfoResult::event(QEvent *event)
+void QHostInfoResult::finalizePostResultsReady(const QHostInfo &info)
{
- if (event->type() == QEvent::MetaCall) {
- Q_ASSERT(slotObj);
- auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
- auto args = metaCallEvent->args();
- // we didn't have a context object, or it's still alive
- if (!withContextObject || receiver)
- slotObj->call(const_cast<QObject*>(receiver.data()), args);
- slotObj->destroyIfLastRef();
-
- deleteLater();
- return true;
+ Q_ASSERT(slotObj);
+
+ // we used to have a context object, but it's already destroyed
+ if (receiver) {
+ void *args[] = { nullptr, const_cast<QHostInfo *>(&info) };
+ slotObj->call(const_cast<QObject *>(receiver.data()), args);
}
- return QObject::event(event);
+
+ deleteLater();
}
/*!
@@ -216,7 +174,7 @@ bool QHostInfoResult::event(QEvent *event)
QHostInfo::localHostName() function.
QHostInfo uses the mechanisms provided by the operating system
- to perform the lookup. As per {https://tools.ietf.org/html/rfc6724}{RFC 6724}
+ to perform the lookup. As per \l {RFC 6724}
there is no guarantee that all IP addresses registered for a domain or
host will be returned.
@@ -227,13 +185,12 @@ bool QHostInfoResult::event(QEvent *event)
\note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
for performance improvements.
- \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492},
- {https://tools.ietf.org/html/rfc6724}{RFC 6724}
+ \sa QAbstractSocket, {RFC 3492}, {RFC 6724}
*/
static int nextId()
{
- static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
return 1 + counter.fetchAndAddRelaxed(1);
}
@@ -264,11 +221,18 @@ static int nextId()
\note There is no guarantee on the order the signals will be emitted
if you start multiple requests with lookupHost().
+ \note In Qt versions prior to 6.7, this function took \a receiver as
+ (non-const) \c{QObject*}.
+
\sa abortHostLookup(), addresses(), error(), fromName()
*/
-int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member)
+int QHostInfo::lookupHost(const QString &name, const QObject *receiver, const char *member)
{
- return QHostInfoPrivate::lookupHostImpl(name, receiver, nullptr, member);
+ if (!receiver || !member) {
+ qWarning("QHostInfo::lookupHost: both the receiver and the member to invoke must be non-null");
+ return -1;
+ }
+ return QHostInfo::lookupHostImpl(name, receiver, nullptr, member);
}
/*!
@@ -293,7 +257,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *me
*/
/*!
- \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor)
+ \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor &&functor)
\since 5.9
@@ -377,32 +341,16 @@ QHostInfo QHostInfo::fromName(const QString &name)
qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
#endif
+#ifdef Q_OS_WASM
+ return QHostInfoAgent::lookup(name);
+#else
QHostInfo hostInfo = QHostInfoAgent::fromName(name);
QHostInfoLookupManager* manager = theHostInfoLookupManager();
manager->cache.put(name, hostInfo);
return hostInfo;
-}
-
-#ifndef QT_NO_BEARERMANAGEMENT
-QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
-{
-#if defined QHOSTINFO_DEBUG
- qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
#endif
-
- QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
- QHostInfoLookupManager* manager = theHostInfoLookupManager();
- manager->cache.put(name, hostInfo);
- return hostInfo;
}
-#endif
-#ifndef QT_NO_BEARERMANAGEMENT
-QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
-{
- return QHostInfoAgent::fromName(hostName);
-}
-#endif
QHostInfo QHostInfoAgent::reverseLookup(const QHostAddress &address)
{
@@ -549,7 +497,7 @@ QHostInfo QHostInfoAgent::lookup(const QString &hostName)
QString tmp;
QList<QHostAddress> addresses = results.addresses();
for (int i = 0; i < addresses.count(); ++i) {
- if (i != 0) tmp += QLatin1String(", ");
+ if (i != 0) tmp += ", "_L1;
tmp += addresses.at(i).toString();
}
qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}",
@@ -595,7 +543,7 @@ QHostInfo::QHostInfo(const QHostInfo &other)
}
/*!
- \fn QHostInfo(QHostInfo &&other)
+ \fn QHostInfo::QHostInfo(QHostInfo &&other)
Move-constructs a new QHostInfo from \a other.
@@ -612,10 +560,11 @@ QHostInfo::QHostInfo(const QHostInfo &other)
*/
QHostInfo &QHostInfo::operator=(const QHostInfo &other)
{
- if (d_ptr)
- *d_ptr = *other.d_ptr;
- else
- d_ptr = new QHostInfoPrivate(*other.d_ptr);
+ if (this == &other)
+ return *this;
+
+ Q_ASSERT(d_ptr && other.d_ptr);
+ *d_ptr = *other.d_ptr;
return *this;
}
@@ -776,32 +725,29 @@ QString QHostInfo::localHostName()
\sa hostName()
*/
-// ### Qt 6 merge with function below
-int QHostInfo::lookupHostImpl(const QString &name,
- const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj)
-{
- return QHostInfoPrivate::lookupHostImpl(name, receiver, slotObj, nullptr);
-}
-/*
+/*!
+ \internal
Called by the various lookupHost overloads to perform the lookup.
- Signals either the functor encapuslated in the \a slotObj in the context
+ Signals either the functor encapuslated in the \a slotObjRaw in the context
of \a receiver, or the \a member slot of the \a receiver.
- \a receiver might be the nullptr, but only if a \a slotObj is provided.
+ \a receiver might be the nullptr, but only if a \a slotObjRaw is provided.
*/
-int QHostInfoPrivate::lookupHostImpl(const QString &name,
- const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj,
- const char *member)
+int QHostInfo::lookupHostImpl(const QString &name,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
+ const char *member)
{
+ QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw};
#if defined QHOSTINFO_DEBUG
- qDebug("QHostInfoPrivate::lookupHostImpl(\"%s\", %p, %p, %s)",
- name.toLatin1().constData(), receiver, slotObj, member ? member + 1 : 0);
+ qDebug("QHostInfo::lookupHostImpl(\"%s\", %p, %p, %s)",
+ name.toLatin1().constData(), receiver, slotObj.get(), member ? member + 1 : 0);
#endif
Q_ASSERT(!member != !slotObj); // one of these must be set, but not both
Q_ASSERT(receiver || slotObj);
+ Q_ASSERT(!member || receiver); // if member is set, also is receiver
+ const bool isUsingStringBasedSlot = static_cast<bool>(member);
if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
qWarning("QHostInfo::lookupHost() called with no event dispatcher");
@@ -817,15 +763,31 @@ int QHostInfoPrivate::lookupHostImpl(const QString &name,
hostInfo.setError(QHostInfo::HostNotFound);
hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(hostInfo);
return id;
}
+#ifdef Q_OS_WASM
+ // Resolve the host name directly without using a thread or cache,
+ // since Emscripten's host lookup is fast. Emscripten maintains an internal
+ // mapping of hosts and addresses for the purposes of WebSocket socket
+ // tunnelling, and does not perform an actual host lookup.
+ QHostInfo hostInfo = QHostInfoAgent::lookup(name);
+ hostInfo.setLookupId(id);
+
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
+ QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
+ receiver, member, Qt::QueuedConnection);
+ }
+ result.postResultsReady(hostInfo);
+#else
QHostInfoLookupManager *manager = theHostInfoLookupManager();
if (Q_LIKELY(manager)) {
@@ -836,32 +798,38 @@ int QHostInfoPrivate::lookupHostImpl(const QString &name,
QHostInfo info = manager->cache.get(name, &valid);
if (valid) {
info.setLookupId(id);
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(info);
return id;
}
}
// cache is not enabled or it was not in the cache, do normal lookup
- QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, slotObj);
- if (receiver && member)
+ QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
manager->scheduleLookup(runnable);
}
+#endif // Q_OS_WASM
return id;
}
QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj) :
- toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj)
+ QtPrivate::SlotObjUniquePtr slotObj)
+ : toBeLookedUp{hn}, id{i}, resultEmitter{receiver, std::move(slotObj)}
{
setAutoDelete(true);
}
+QHostInfoRunnable::~QHostInfoRunnable()
+ = default;
+
// the QHostInfoLookupManager will at some point call this via a QThreadPool
void QHostInfoRunnable::run()
{
@@ -971,7 +939,7 @@ void QHostInfoLookupManager::rescheduleWithMutexHeld()
if (!finishedLookups.isEmpty()) {
// remove ID from aborted if it is in there
- for (int i = 0; i < finishedLookups.length(); i++) {
+ for (int i = 0; i < finishedLookups.size(); i++) {
abortedLookups.removeAll(finishedLookups.at(i)->id);
}
@@ -999,7 +967,7 @@ void QHostInfoLookupManager::rescheduleWithMutexHeld()
isAlreadyRunning).second,
scheduledLookups.end());
- const int availableThreads = threadPool.maxThreadCount() - currentLookups.size();
+ const int availableThreads = std::max(threadPool.maxThreadCount(), 1) - currentLookups.size();
if (availableThreads > 0) {
int readyToStartCount = qMin(availableThreads, scheduledLookups.size());
auto it = scheduledLookups.begin();
@@ -1037,9 +1005,12 @@ void QHostInfoLookupManager::abortLookup(int id)
if (wasDeleted)
return;
+ if (id == -1)
+ return;
+
#if QT_CONFIG(thread)
// is postponed? delete and return
- for (int i = 0; i < postponedLookups.length(); i++) {
+ for (int i = 0; i < postponedLookups.size(); i++) {
if (postponedLookups.at(i)->id == id) {
delete postponedLookups.takeAt(i);
return;
@@ -1048,7 +1019,7 @@ void QHostInfoLookupManager::abortLookup(int id)
#endif
// is scheduled? delete and return
- for (int i = 0; i < scheduledLookups.length(); i++) {
+ for (int i = 0; i < scheduledLookups.size(); i++) {
if (scheduledLookups.at(i)->id == id) {
delete scheduledLookups.takeAt(i);
return;
@@ -1101,7 +1072,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char
}
// was not in cache, trigger lookup
- *id = QHostInfoPrivate::lookupHostImpl(name, receiver, nullptr, member);
+ *id = QHostInfo::lookupHostImpl(name, receiver, nullptr, member);
// return empty response, valid==false
return QHostInfo();
@@ -1183,3 +1154,5 @@ void QHostInfoCache::clear()
}
QT_END_NAMESPACE
+
+#include "moc_qhostinfo_p.cpp"
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index cda286b423..3942e41498 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHOSTINFO_H
#define QHOSTINFO_H
@@ -53,6 +17,7 @@ class QHostInfoPrivate;
class Q_NETWORK_EXPORT QHostInfo
{
+ Q_GADGET
public:
enum HostInfoError {
NoError,
@@ -62,12 +27,12 @@ public:
explicit QHostInfo(int lookupId = -1);
QHostInfo(const QHostInfo &d);
- QHostInfo(QHostInfo &&other) noexcept : d_ptr(qExchange(other.d_ptr, nullptr)) {}
+ QHostInfo(QHostInfo &&other) noexcept : d_ptr(std::exchange(other.d_ptr, nullptr)) {}
QHostInfo &operator=(const QHostInfo &d);
QHostInfo &operator=(QHostInfo &&other) noexcept { swap(other); return *this; }
~QHostInfo();
- void swap(QHostInfo &other) noexcept { qSwap(d_ptr, other.d_ptr); }
+ void swap(QHostInfo &other) noexcept { qt_ptr_swap(d_ptr, other.d_ptr); }
QString hostName() const;
void setHostName(const QString &name);
@@ -84,68 +49,42 @@ public:
void setLookupId(int id);
int lookupId() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static int lookupHost(const QString &name, QObject *receiver, const char *member);
+#endif
+ static int lookupHost(const QString &name, const QObject *receiver, const char *member);
static void abortHostLookup(int lookupId);
static QHostInfo fromName(const QString &name);
static QString localHostName();
static QString localDomainName();
-#ifdef Q_CLANG_QDOC
- template<typename Functor>
- static int lookupHost(const QString &name, Functor functor);
+#ifdef Q_QDOC
template<typename Functor>
static int lookupHost(const QString &name, const QObject *context, Functor functor);
#else
- // lookupHost to a QObject slot
- template <typename Func>
+ // lookupHost to a callable (with context)
+ template <typename Functor>
static inline int lookupHost(const QString &name,
- const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
- Func slot)
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&func)
{
- typedef QtPrivate::FunctionPointer<Func> SlotType;
-
- typedef QtPrivate::FunctionPointer<void (*)(QHostInfo)> SignalType;
- Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
- "The slot requires more arguments than the signal provides.");
- Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments,
- typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType,
- typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible "
- "with the return type of the signal.");
-
- auto slotObj = new QtPrivate::QSlotObject<Func, typename SlotType::Arguments, void>(slot);
- return lookupHostImpl(name, receiver, slotObj);
+ using Prototype = void(*)(QHostInfo);
+ QtPrivate::AssertCompatibleFunctions<Prototype, Functor>();
+ return lookupHostImpl(name, receiver,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(func)),
+ nullptr);
}
+#endif // Q_QDOC
+#ifndef QT_NO_CONTEXTLESS_CONNECT
// lookupHost to a callable (without context)
- template <typename Func>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction &&
- !std::is_same<const char *, Func>::value, int>::type
- lookupHost(const QString &name, Func slot)
+ template <typename Functor>
+ static inline int lookupHost(const QString &name, Functor &&slot)
{
- return lookupHost(name, nullptr, std::move(slot));
+ return lookupHost(name, nullptr, std::forward<Functor>(slot));
}
-
- // lookupHost to a functor or function pointer (with context)
- template <typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, int>::type
- lookupHost(const QString &name, QObject *context, Func1 slot)
- {
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
-
- Q_STATIC_ASSERT_X(int(SlotType::ArgumentCount) <= 1,
- "The slot must not require more than one argument");
-
- auto slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
- typename QtPrivate::List<QHostInfo>,
- void>(std::move(slot));
- return lookupHostImpl(name, context, slotObj);
- }
-#endif // Q_QDOC
+#endif // QT_NO_CONTEXTLESS_CONNECT
private:
QHostInfoPrivate *d_ptr;
@@ -153,13 +92,17 @@ private:
static int lookupHostImpl(const QString &name,
const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj);
+ QtPrivate::QSlotObjectBase *slotObj,
+ const char *member);
+
+ friend QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver,
+ const char *member, bool *valid, int *id);
};
-Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QHostInfo)
+Q_DECLARE_SHARED(QHostInfo)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QHostInfo)
+QT_DECL_METATYPE_EXTERN(QHostInfo, Q_NETWORK_EXPORT)
#endif // QHOSTINFO_H
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 1798ceab0a..b229eb1cd8 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHOSTINFO_P_H
#define QHOSTINFO_P_H
@@ -70,9 +34,6 @@
#include <QElapsedTimer>
#include <QCache>
-#include <QNetworkSession>
-#include <QSharedPointer>
-
#include <atomic>
QT_BEGIN_NAMESPACE
@@ -82,26 +43,21 @@ class QHostInfoResult : public QObject
{
Q_OBJECT
public:
- QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
- : receiver(receiver), slotObj(slotObj),
- withContextObject(slotObj && receiver)
- {
- if (receiver)
- moveToThread(receiver->thread());
- }
+ explicit QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot);
+ ~QHostInfoResult() override;
void postResultsReady(const QHostInfo &info);
Q_SIGNALS:
void resultsReady(const QHostInfo &info);
-protected:
- bool event(QEvent *event) override;
+private Q_SLOTS:
+ void finalizePostResultsReady(const QHostInfo &info);
private:
- QHostInfoResult(const QHostInfoResult *other)
- : receiver(other->receiver), slotObj(other->slotObj),
- withContextObject(other->withContextObject)
+ QHostInfoResult(QHostInfoResult *other)
+ : receiver(other->receiver.get() != other ? other->receiver.get() : this),
+ slotObj{std::move(other->slotObj)}
{
// cleanup if the application terminates before results are delivered
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
@@ -110,19 +66,16 @@ private:
moveToThread(other->thread());
}
+ // receiver is either a QObject provided by the user,
+ // or it's set to `this` (to emulate the behavior of the contextless connect())
QPointer<const QObject> receiver = nullptr;
- QtPrivate::QSlotObjectBase *slotObj = nullptr;
- const bool withContextObject = false;
+ QtPrivate::SlotObjUniquePtr slotObj;
};
class QHostInfoAgent
{
public:
static QHostInfo fromName(const QString &hostName);
-#ifndef QT_NO_BEARERMANAGEMENT
- static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession);
-#endif
-private:
static QHostInfo lookup(const QString &hostName);
static QHostInfo reverseLookup(const QHostAddress &address);
};
@@ -132,14 +85,10 @@ class QHostInfoPrivate
public:
inline QHostInfoPrivate()
: err(QHostInfo::NoError),
- errorStr(QLatin1String(QT_TRANSLATE_NOOP("QHostInfo", "Unknown error"))),
+ errorStr(QLatin1StringView(QT_TRANSLATE_NOOP("QHostInfo", "Unknown error"))),
lookupId(0)
{
}
-#ifndef QT_NO_BEARERMANAGEMENT
- //not a public API yet
- static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession);
-#endif
static int lookupHostImpl(const QString &name,
const QObject *receiver,
QtPrivate::QSlotObjectBase *slotObj,
@@ -188,8 +137,10 @@ private:
class QHostInfoRunnable : public QRunnable
{
public:
- QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj);
+ explicit QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::SlotObjUniquePtr slotObj);
+ ~QHostInfoRunnable() override;
+
void run() override;
QString toBeLookedUp;
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 625fbabf31..80d386a13d 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -1,173 +1,79 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QHOSTINFO_DEBUG
-#include "qplatformdefs.h"
-
#include "qhostinfo_p.h"
-#include "private/qnativesocketengine_p.h"
-#include "qiodevice.h"
+
#include <qbytearray.h>
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qbasicatomic.h>
-#include <qurl.h>
#include <qfile.h>
-#include <private/qnet_unix_p.h>
+#include <qplatformdefs.h>
+#include <qurl.h>
#include <sys/types.h>
#include <netdb.h>
-#include <arpa/inet.h>
-#if defined(Q_OS_VXWORKS)
-# include <hostLib.h>
-#else
-# include <resolv.h>
-#endif
+#include <netinet/in.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
+#if QT_CONFIG(libresolv)
+# include <resolv.h>
#endif
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef _PATH_RESCONF
+# define _PATH_RESCONF "/etc/resolv.conf"
#endif
QT_BEGIN_NAMESPACE
-enum LibResolvFeature {
- NeedResInit,
- NeedResNInit
-};
-
-typedef struct __res_state *res_state_ptr;
+using namespace Qt::StringLiterals;
-typedef int (*res_init_proto)(void);
-static res_init_proto local_res_init = nullptr;
-typedef int (*res_ninit_proto)(res_state_ptr);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef void (*res_nclose_proto)(res_state_ptr);
-static res_nclose_proto local_res_nclose = nullptr;
-static res_state_ptr local_res = nullptr;
-
-#if QT_CONFIG(library) && !defined(Q_OS_QNX)
-namespace {
-struct LibResolv
+static void maybeRefreshResolver()
{
- enum {
-#ifdef RES_NORELOAD
- // If RES_NORELOAD is defined, then the libc is capable of watching
- // /etc/resolv.conf for changes and reloading as necessary. So accept
- // whatever is configured.
- ReinitNecessary = false
-#else
- ReinitNecessary = true
+#if defined(RES_NORELOAD)
+ // If RES_NORELOAD is defined, then the libc is capable of watching
+ // /etc/resolv.conf for changes and reloading as necessary. So accept
+ // whatever is configured.
+ return;
+#elif defined(Q_OS_DARWIN)
+ // Apple's libsystem_info.dylib:getaddrinfo() uses the
+ // libsystem_dnssd.dylib to resolve hostnames. Using res_init() has no
+ // effect on it and is thread-unsafe.
+ return;
+#elif defined(Q_OS_FREEBSD)
+ // FreeBSD automatically refreshes:
+ // https://github.com/freebsd/freebsd-src/blob/b3fe5d932264445cbf9a1c4eab01afb6179b499b/lib/libc/resolv/res_state.c#L69
+ return;
+#elif defined(Q_OS_OPENBSD)
+ // OpenBSD automatically refreshes:
+ // https://github.com/ligurio/openbsd-src/blob/b1ce0da17da254cc15b8aff25b3d55d3c7a82cec/lib/libc/asr/asr.c#L367
+ return;
+#elif defined(Q_OS_QNX)
+ // res_init() is not thread-safe; executing it leads to state corruption.
+ // Whether it reloads resolv.conf on its own is unknown.
+ return;
#endif
- };
- QLibrary lib;
- LibResolv();
- ~LibResolv() { lib.unload(); }
-};
-}
-
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
-{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
-#else
- return nullptr;
-#endif
-}
-
-LibResolv::LibResolv()
-{
- QLibrary lib;
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
-#endif
- {
- lib.setFileName(QLatin1String("resolv"));
- lib.load();
- }
-
- // res_ninit is required for localDomainName()
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
- if (local_res_ninit) {
- // we must now find res_nclose
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_ninit = nullptr;
- }
-
- if (ReinitNecessary || !local_res_ninit) {
- local_res_init = res_init_proto(resolveSymbol(lib, "__res_init"));
- if (!local_res_init)
- local_res_init = res_init_proto(resolveSymbol(lib, "res_init"));
-
- if (local_res_init && !local_res_ninit) {
- // if we can't get a thread-safe context, we have to use the global _res state
- local_res = res_state_ptr(resolveSymbol(lib, "_res"));
+#if QT_CONFIG(libresolv)
+ // OSes known or thought to reach here: AIX, NetBSD, Solaris,
+ // Linux with MUSL (though res_init() does nothing and is unnecessary)
+
+ Q_CONSTINIT static QT_STATBUF lastStat = {};
+ Q_CONSTINIT static QBasicMutex mutex = {};
+ if (QT_STATBUF st; QT_STAT(_PATH_RESCONF, &st) == 0) {
+ QMutexLocker locker(&mutex);
+ bool refresh = false;
+ if ((_res.options & RES_INIT) == 0)
+ refresh = true;
+ else if (lastStat.st_ctime != st.st_ctime)
+ refresh = true; // file was updated
+ else if (lastStat.st_dev != st.st_dev || lastStat.st_ino != st.st_ino)
+ refresh = true; // file was replaced
+ if (refresh) {
+ lastStat = st;
+ res_init();
}
}
+#endif
}
-Q_GLOBAL_STATIC(LibResolv, libResolv)
-
-static void resolveLibrary(LibResolvFeature f)
-{
- if (LibResolv::ReinitNecessary || f == NeedResNInit)
- libResolv();
-}
-#else // QT_CONFIG(library) || Q_OS_QNX
-static void resolveLibrary(LibResolvFeature)
-{
-}
-#endif // QT_CONFIG(library) || Q_OS_QNX
QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
@@ -178,12 +84,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
hostName.toLatin1().constData());
#endif
- // Load res_init on demand.
- resolveLibrary(NeedResInit);
-
- // If res_init is available, poll it.
- if (local_res_init)
- local_res_init();
+ maybeRefreshResolver();
QHostAddress address;
if (address.setAddress(hostName))
@@ -194,56 +95,49 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QString QHostInfo::localDomainName()
{
-#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
- resolveLibrary(NeedResNInit);
- if (local_res_ninit) {
- // using thread-safe version
- res_state_ptr state = res_state_ptr(malloc(sizeof(*state)));
- Q_CHECK_PTR(state);
- memset(state, 0, sizeof(*state));
- local_res_ninit(state);
- QString domainName = QUrl::fromAce(state->defdname);
+#if QT_CONFIG(libresolv)
+ auto domainNameFromRes = [](res_state r) {
+ QString domainName;
+ if (r->defdname[0])
+ domainName = QUrl::fromAce(r->defdname);
if (domainName.isEmpty())
- domainName = QUrl::fromAce(state->dnsrch[0]);
- local_res_nclose(state);
- free(state);
-
+ domainName = QUrl::fromAce(r->dnsrch[0]);
return domainName;
+ };
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) == 0) {
+ // using thread-safe version
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+ return domainNameFromRes(&state);
}
- if (local_res_init && local_res) {
- // using thread-unsafe version
+ // using thread-unsafe version
+ maybeRefreshResolver();
+ return domainNameFromRes(&_res);
+#endif // !QT_CONFIG(libresolv)
- local_res_init();
- QString domainName = QUrl::fromAce(local_res->defdname);
- if (domainName.isEmpty())
- domainName = QUrl::fromAce(local_res->dnsrch[0]);
- return domainName;
- }
-#endif
// nothing worked, try doing it by ourselves:
QFile resolvconf;
-#if defined(_PATH_RESCONF)
- resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
-#else
- resolvconf.setFileName(QLatin1String("/etc/resolv.conf"));
-#endif
+ resolvconf.setFileName(_PATH_RESCONF ""_L1);
if (!resolvconf.open(QIODevice::ReadOnly))
return QString(); // failure
QString domainName;
while (!resolvconf.atEnd()) {
- QByteArray line = resolvconf.readLine().trimmed();
- if (line.startsWith("domain "))
- return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
+ const QByteArray lineArray = resolvconf.readLine();
+ QByteArrayView line = QByteArrayView(lineArray).trimmed();
+ constexpr QByteArrayView domainWithSpace = "domain ";
+ if (line.startsWith(domainWithSpace))
+ return QUrl::fromAce(line.mid(domainWithSpace.size()).trimmed().toByteArray());
// in case there's no "domain" line, fall back to the first "search" entry
- if (domainName.isEmpty() && line.startsWith("search ")) {
- QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
+ constexpr QByteArrayView searchWithSpace = "search ";
+ if (domainName.isEmpty() && line.startsWith(searchWithSpace)) {
+ QByteArrayView searchDomain = line.mid(searchWithSpace.size()).trimmed();
int pos = searchDomain.indexOf(' ');
if (pos != -1)
searchDomain.truncate(pos);
- domainName = QUrl::fromAce(searchDomain);
+ domainName = QUrl::fromAce(searchDomain.toByteArray());
}
}
diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp
index 0b5cc98970..61a1983e9d 100644
--- a/src/network/kernel/qhostinfo_win.cpp
+++ b/src/network/kernel/qhostinfo_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <winsock2.h>
diff --git a/src/network/kernel/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm
index f6daf9ed50..60b3cd6581 100644
--- a/src/network/kernel/qnetconmonitor_darwin.mm
+++ b/src/network/kernel/qnetconmonitor_darwin.mm
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "private/qnativesocketengine_p.h"
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "private/qnativesocketengine_p_p.h"
#include "private/qnetconmonitor_p.h"
#include "private/qobject_p.h"
@@ -124,6 +88,9 @@ public:
void updateState(SCNetworkReachabilityFlags newState);
void reset();
bool isReachable() const;
+#ifdef QT_PLATFORM_UIKIT
+ bool isWwan() const;
+#endif
static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info);
@@ -139,9 +106,19 @@ void QNetworkConnectionMonitorPrivate::updateState(SCNetworkReachabilityFlags ne
// is set. There are more possible flags that require more tests/some special
// setup. So in future this part and related can change/be extended.
const bool wasReachable = isReachable();
+
+#ifdef QT_PLATFORM_UIKIT
+ const bool hadWwan = isWwan();
+#endif
+
state = newState;
if (wasReachable != isReachable())
emit q->reachabilityChanged(isReachable());
+
+#ifdef QT_PLATFORM_UIKIT
+ if (hadWwan != isWwan())
+ emit q->isWwanChanged(isWwan());
+#endif
}
void QNetworkConnectionMonitorPrivate::reset()
@@ -160,6 +137,13 @@ bool QNetworkConnectionMonitorPrivate::isReachable() const
return !!(state & kSCNetworkReachabilityFlagsReachable);
}
+#ifdef QT_PLATFORM_UIKIT // The IsWWAN flag is not available on macOS
+bool QNetworkConnectionMonitorPrivate::isWwan() const
+{
+ return !!(state & kSCNetworkReachabilityFlagsIsWWAN);
+}
+#endif
+
void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info)
{
// To be executed only on the reachability queue.
@@ -208,7 +192,7 @@ bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHos
qt_sockaddr client = qt_hostaddress_to_sockaddr(local);
if (remote.isNull()) {
- // That's a special case our QNetworkStatusMonitor is using (AnyIpv4/6 address to check an overall status).
+ // That's a special case our QNetworkInformation backend is using (AnyIpv4/6 address to check an overall status).
d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client));
} else {
qt_sockaddr target = qt_hostaddress_to_sockaddr(remote);
@@ -301,119 +285,28 @@ bool QNetworkConnectionMonitor::isReachable()
return d->isReachable();
}
-class QNetworkStatusMonitorPrivate : public QObjectPrivate
+#ifdef QT_PLATFORM_UIKIT
+bool QNetworkConnectionMonitor::isWwan() const
{
-public:
- QNetworkConnectionMonitor ipv4Probe;
- bool isOnlineIpv4 = false;
- QNetworkConnectionMonitor ipv6Probe;
- bool isOnlineIpv6 = false;
-};
-
-QNetworkStatusMonitor::QNetworkStatusMonitor()
- : QObject(*new QNetworkStatusMonitorPrivate)
-{
- Q_D(QNetworkStatusMonitor);
-
- if (d->ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) {
- // We manage to create SCNetworkReachabilityRef for IPv4, let's
- // read the last known state then!
- d->isOnlineIpv4 = d->ipv4Probe.isReachable();
- }
-
- if (d->ipv6Probe.setTargets(QHostAddress::AnyIPv6, {})) {
- // We manage to create SCNetworkReachability ref for IPv6, let's
- // read the last known state then!
- d->isOnlineIpv6 = d->ipv6Probe.isReachable();
- }
-
-
- connect(&d->ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
- &QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection);
- connect(&d->ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
- &QNetworkStatusMonitor::reachabilityChanged, Qt::QueuedConnection);
-}
-
-QNetworkStatusMonitor::~QNetworkStatusMonitor()
-{
- Q_D(QNetworkStatusMonitor);
-
- d->ipv4Probe.disconnect();
- d->ipv4Probe.stopMonitoring();
- d->ipv6Probe.disconnect();
- d->ipv6Probe.stopMonitoring();
-}
-
-bool QNetworkStatusMonitor::start()
-{
- Q_D(QNetworkStatusMonitor);
+ Q_D(const QNetworkConnectionMonitor);
if (isMonitoring()) {
- qCWarning(lcNetMon, "Network status monitor is already active");
- return true;
+ qCWarning(lcNetMon, "Calling isWwan() is unsafe after the monitoring started");
+ return false;
}
- d->ipv4Probe.startMonitoring();
- d->ipv6Probe.startMonitoring();
-
- return isMonitoring();
-}
-
-void QNetworkStatusMonitor::stop()
-{
- Q_D(QNetworkStatusMonitor);
-
- if (d->ipv4Probe.isMonitoring())
- d->ipv4Probe.stopMonitoring();
- if (d->ipv6Probe.isMonitoring())
- d->ipv6Probe.stopMonitoring();
-}
-
-bool QNetworkStatusMonitor::isMonitoring() const
-{
- Q_D(const QNetworkStatusMonitor);
-
- return d->ipv4Probe.isMonitoring() || d->ipv6Probe.isMonitoring();
-}
-
-bool QNetworkStatusMonitor::isNetworkAccessible()
-{
- // This function is to be executed on the thread that created
- // and uses 'this'.
- Q_D(QNetworkStatusMonitor);
+ if (!d->probe) {
+ qCWarning(lcNetMon, "Medium is unknown, set the target first");
+ return false;
+ }
- return d->isOnlineIpv4 || d->isOnlineIpv6;
+ return d->isWwan();
}
+#endif
-bool QNetworkStatusMonitor::isEnabled()
+bool QNetworkConnectionMonitor::isEnabled()
{
return true;
}
-void QNetworkStatusMonitor::reachabilityChanged(bool online)
-{
- // This function is executed on the thread that created/uses 'this',
- // not on the reachability queue.
- Q_D(QNetworkStatusMonitor);
-
- auto probe = qobject_cast<QNetworkConnectionMonitor *>(sender());
- if (!probe)
- return;
-
- const bool isIpv4 = probe == &d->ipv4Probe;
- bool &probeOnline = isIpv4 ? d->isOnlineIpv4 : d->isOnlineIpv6;
- bool otherOnline = isIpv4 ? d->isOnlineIpv6 : d->isOnlineIpv4;
-
- if (probeOnline == online) {
- // We knew this already?
- return;
- }
-
- probeOnline = online;
- if (!otherOnline) {
- // We either just lost or got a network access.
- emit onlineStateChanged(probeOnline);
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetconmonitor_p.h b/src/network/kernel/qnetconmonitor_p.h
index 282bac5081..26211bb770 100644
--- a/src/network/kernel/qnetconmonitor_p.h
+++ b/src/network/kernel/qnetconmonitor_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETCONMONITOR_P_H
#define QNETCONMONITOR_P_H
@@ -61,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QNetworkConnectionMonitorPrivate;
-class Q_AUTOTEST_EXPORT QNetworkConnectionMonitor : public QObject
+class Q_NETWORK_EXPORT QNetworkConnectionMonitor : public QObject
{
Q_OBJECT
@@ -73,52 +37,33 @@ public:
bool setTargets(const QHostAddress &local, const QHostAddress &remote);
bool isReachable();
- // Important: on Darwin you should not call isReachable() after
+#ifdef QT_PLATFORM_UIKIT
+ bool isWwan() const;
+#endif
+
+ // Important: on Darwin you should not call isReachable/isWwan() after
// startMonitoring(), you have to listen to reachabilityChanged()
// signal instead.
bool startMonitoring();
bool isMonitoring() const;
void stopMonitoring();
+ static bool isEnabled();
+
Q_SIGNALS:
// Important: connect to this using QueuedConnection. On Darwin
// callback is coming on a special dispatch queue.
void reachabilityChanged(bool isOnline);
+#ifdef QT_PLATFORM_UIKIT
+ void isWwanChanged(bool isWwan);
+#endif
+
private:
Q_DECLARE_PRIVATE(QNetworkConnectionMonitor)
Q_DISABLE_COPY_MOVE(QNetworkConnectionMonitor)
};
-class QNetworkStatusMonitorPrivate;
-class Q_AUTOTEST_EXPORT QNetworkStatusMonitor : public QObject
-{
- Q_OBJECT
-
-public:
- QNetworkStatusMonitor();
- ~QNetworkStatusMonitor();
-
- bool isNetworkAccessible();
-
- bool start();
- void stop();
- bool isMonitoring() const;
-
- static bool isEnabled();
-
-Q_SIGNALS:
- // Unlike QNetworkConnectionMonitor, this can be connected to directly.
- void onlineStateChanged(bool isOnline);
-
-private slots:
- void reachabilityChanged(bool isOnline);
-
-private:
- Q_DECLARE_PRIVATE(QNetworkStatusMonitor)
- Q_DISABLE_COPY_MOVE(QNetworkStatusMonitor)
-};
-
Q_DECLARE_LOGGING_CATEGORY(lcNetMon)
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetconmonitor_stub.cpp b/src/network/kernel/qnetconmonitor_stub.cpp
index 1ad4e9ba5a..d3f1224e86 100644
--- a/src/network/kernel/qnetconmonitor_stub.cpp
+++ b/src/network/kernel/qnetconmonitor_stub.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetconmonitor_p.h"
@@ -45,7 +9,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
-// Note: this 'stub' version is never enabled (see QNetworkStatusMonitor::isEnabled below)
+// Note: this 'stub' version is never enabled (see QNetworkConnectionMonitor::isEnabled below)
// and thus should never affect QNAM in any unusuall way. Having this 'stub' version is similar
// to building Qt with bearer management configured out.
@@ -61,8 +25,8 @@ QNetworkConnectionMonitor::QNetworkConnectionMonitor()
QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote)
: QObject(*new QNetworkConnectionMonitorPrivate)
{
- Q_UNUSED(local)
- Q_UNUSED(remote)
+ Q_UNUSED(local);
+ Q_UNUSED(remote);
}
QNetworkConnectionMonitor::~QNetworkConnectionMonitor()
@@ -71,8 +35,8 @@ QNetworkConnectionMonitor::~QNetworkConnectionMonitor()
bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote)
{
- Q_UNUSED(local)
- Q_UNUSED(remote)
+ Q_UNUSED(local);
+ Q_UNUSED(remote);
return false;
}
@@ -96,46 +60,9 @@ bool QNetworkConnectionMonitor::isReachable()
return false;
}
-class QNetworkStatusMonitorPrivate : public QObjectPrivate
-{
-};
-
-QNetworkStatusMonitor::QNetworkStatusMonitor()
- : QObject(*new QNetworkStatusMonitorPrivate)
-{
-}
-
-QNetworkStatusMonitor::~QNetworkStatusMonitor()
-{
-}
-
-bool QNetworkStatusMonitor::start()
+bool QNetworkConnectionMonitor::isEnabled()
{
return false;
}
-void QNetworkStatusMonitor::stop()
-{
-}
-
-bool QNetworkStatusMonitor::isMonitoring() const
-{
- return false;
-}
-
-bool QNetworkStatusMonitor::isNetworkAccessible()
-{
- return false;
-}
-
-bool QNetworkStatusMonitor::isEnabled()
-{
- return false;
-}
-
-void QNetworkStatusMonitor::reachabilityChanged(bool online)
-{
- Q_UNUSED(online)
-}
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp
index 1566e7f914..bf6aff1e46 100644
--- a/src/network/kernel/qnetconmonitor_win.cpp
+++ b/src/network/kernel/qnetconmonitor_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetconmonitor_p.h"
@@ -44,13 +8,15 @@
#include <QtCore/quuid.h>
#include <QtCore/qmetaobject.h>
+#include <QtCore/private/qfunctions_win_p.h>
+#include <QtCore/private/qsystemerror_p.h>
+
#include <QtNetwork/qnetworkinterface.h>
#include <objbase.h>
#include <netlistmgr.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
-#include <comdef.h>
#include <iphlpapi.h>
#include <algorithm>
@@ -62,12 +28,6 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
namespace {
-QString errorStringFromHResult(HRESULT hr)
-{
- _com_error error(hr);
- return QString::fromWCharArray(error.ErrorMessage());
-}
-
template<typename T>
bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
{
@@ -92,7 +52,7 @@ QNetworkInterface getInterfaceFromHostAddress(const QHostAddress &local)
});
});
if (it == interfaces.cend()) {
- qCWarning(lcNetMon, "Could not find the interface for the local address.");
+ qCDebug(lcNetMon, "Could not find the interface for the local address.");
return {};
}
return *it;
@@ -122,11 +82,11 @@ public:
HRESULT STDMETHODCALLTYPE NetworkConnectionPropertyChanged(
GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) override;
- Q_REQUIRED_RESULT
+ [[nodiscard]]
bool setTarget(const QNetworkInterface &iface);
- Q_REQUIRED_RESULT
+ [[nodiscard]]
bool startMonitoring();
- Q_REQUIRED_RESULT
+ [[nodiscard]]
bool stopMonitoring();
private:
@@ -151,25 +111,29 @@ public:
QNetworkConnectionMonitorPrivate();
~QNetworkConnectionMonitorPrivate();
- Q_REQUIRED_RESULT
+ [[nodiscard]]
bool setTargets(const QHostAddress &local, const QHostAddress &remote);
- Q_REQUIRED_RESULT
+ [[nodiscard]]
bool startMonitoring();
void stopMonitoring();
void setConnectivity(NLM_CONNECTIVITY newConnectivity);
private:
+ QComHelper comHelper;
+
ComPtr<QNetworkConnectionEvents> connectionEvents;
// We can assume we have access to internet/subnet when this class is created because
// connection has already been established to the peer:
- NLM_CONNECTIVITY connectivity =
- NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
- | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET);
+ NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY(
+ NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
+ | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET
+ | NLM_CONNECTIVITY_IPV4_LOCALNETWORK | NLM_CONNECTIVITY_IPV6_LOCALNETWORK
+ | NLM_CONNECTIVITY_IPV4_NOTRAFFIC | NLM_CONNECTIVITY_IPV6_NOTRAFFIC);
bool sameSubnet = false;
+ bool isLinkLocal = false;
bool monitoring = false;
- bool comInitFailed = false;
bool remoteIsIPv6 = false;
};
@@ -179,8 +143,8 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
IID_INetworkListManager, &networkListManager);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Could not get a NetworkListManager instance:"
+ << QSystemError::windowsComString(hr);
return;
}
@@ -191,8 +155,8 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
&connectionPoint);
}
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to get connection point for network events:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get connection point for network events:"
+ << QSystemError::windowsComString(hr);
}
}
@@ -203,27 +167,33 @@ QNetworkConnectionEvents::~QNetworkConnectionEvents()
ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid)
{
+ if (!networkListManager) {
+ qCDebug(lcNetMon) << "Failed to enumerate network connections:"
+ << "NetworkListManager was not instantiated";
+ return nullptr;
+ }
+
ComPtr<IEnumNetworkConnections> connections;
auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf());
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to enumerate network connections:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to enumerate network connections:"
+ << QSystemError::windowsComString(hr);
return nullptr;
}
ComPtr<INetworkConnection> connection = nullptr;
do {
hr = connections->Next(1, connection.GetAddressOf(), nullptr);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to get next network connection in enumeration:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get next network connection in enumeration:"
+ << QSystemError::windowsComString(hr);
break;
}
if (connection) {
GUID adapterId;
hr = connection->GetAdapterId(&adapterId);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to get adapter ID from network connection:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get adapter ID from network connection:"
+ << QSystemError::windowsComString(hr);
continue;
}
if (guid == adapterId)
@@ -273,22 +243,23 @@ bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface)
NET_LUID luid;
if (ConvertInterfaceIndexToLuid(iface.index(), &luid) != NO_ERROR) {
- qCWarning(lcNetMon, "Could not get the LUID for the interface.");
+ qCDebug(lcNetMon, "Could not get the LUID for the interface.");
return false;
}
GUID guid;
if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
- qCWarning(lcNetMon, "Could not get the GUID for the interface.");
+ qCDebug(lcNetMon, "Could not get the GUID for the interface.");
return false;
}
ComPtr<INetworkConnection> connection = getNetworkConnectionFromAdapterGuid(guid);
if (!connection) {
- qCWarning(lcNetMon, "Could not get the INetworkConnection instance for the adapter GUID.");
+ qCDebug(lcNetMon, "Could not get the INetworkConnection instance for the adapter GUID.");
return false;
}
auto hr = connection->GetConnectionId(&guid);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get the connection's GUID:"
+ << QSystemError::windowsComString(hr);
return false;
}
currentConnectionId = guid;
@@ -299,19 +270,19 @@ bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface)
bool QNetworkConnectionEvents::startMonitoring()
{
if (currentConnectionId.isNull()) {
- qCWarning(lcNetMon, "Can not start monitoring, set targets first");
+ qCDebug(lcNetMon, "Can not start monitoring, set targets first");
return false;
}
if (!connectionPoint) {
- qCWarning(lcNetMon,
+ qCDebug(lcNetMon,
"We don't have the connection point, cannot start listening to events!");
return false;
}
auto hr = connectionPoint->Advise(this, &cookie);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to subscribe to network connectivity events:"
+ << QSystemError::windowsComString(hr);
return false;
}
return true;
@@ -321,8 +292,8 @@ bool QNetworkConnectionEvents::stopMonitoring()
{
auto hr = connectionPoint->Unadvise(cookie);
if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to unsubscribe from network connection events:"
- << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to unsubscribe from network connection events:"
+ << QSystemError::windowsComString(hr);
return false;
}
cookie = 0;
@@ -332,30 +303,25 @@ bool QNetworkConnectionEvents::stopMonitoring()
QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate()
{
- auto hr = CoInitialize(nullptr);
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
- comInitFailed = true;
+ if (!comHelper.isValid())
return;
- }
connectionEvents = new QNetworkConnectionEvents(this);
}
QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate()
{
- if (comInitFailed)
+ if (!comHelper.isValid())
return;
if (monitoring)
stopMonitoring();
connectionEvents.Reset();
- CoUninitialize();
}
bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local,
const QHostAddress &remote)
{
- if (comInitFailed)
+ if (!comHelper.isValid())
return false;
QNetworkInterface iface = getInterfaceFromHostAddress(local);
@@ -366,10 +332,11 @@ bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local,
addressEntries.cbegin(), addressEntries.cend(),
[&local](const QNetworkAddressEntry &entry) { return entry.ip() == local; });
if (Q_UNLIKELY(it == addressEntries.cend())) {
- qCWarning(lcNetMon, "The address entry we were working with disappeared");
+ qCDebug(lcNetMon, "The address entry we were working with disappeared");
return false;
}
sameSubnet = remote.isInSubnet(local, it->prefixLength());
+ isLinkLocal = remote.isLinkLocal() && local.isLinkLocal();
remoteIsIPv6 = remote.protocol() == QAbstractSocket::IPv6Protocol;
return connectionEvents->setTarget(iface);
@@ -419,11 +386,11 @@ QNetworkConnectionMonitor::~QNetworkConnectionMonitor() = default;
bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote)
{
if (isMonitoring()) {
- qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
+ qCDebug(lcNetMon, "Monitor is already active, call stopMonitoring() first");
return false;
}
if (local.isNull()) {
- qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target");
+ qCDebug(lcNetMon, "Invalid (null) local address, cannot create a reachability target");
return false;
}
// Silently return false for loopback addresses instead of printing warnings later
@@ -437,7 +404,7 @@ bool QNetworkConnectionMonitor::startMonitoring()
{
Q_D(QNetworkConnectionMonitor);
if (isMonitoring()) {
- qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
+ qCDebug(lcNetMon, "Monitor is already active, call stopMonitoring() first");
return false;
}
return d->startMonitoring();
@@ -452,7 +419,7 @@ void QNetworkConnectionMonitor::stopMonitoring()
{
Q_D(QNetworkConnectionMonitor);
if (!isMonitoring()) {
- qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!");
+ qCDebug(lcNetMon, "stopMonitoring was called when not monitoring!");
return;
}
d->stopMonitoring();
@@ -461,252 +428,34 @@ void QNetworkConnectionMonitor::stopMonitoring()
bool QNetworkConnectionMonitor::isReachable()
{
Q_D(QNetworkConnectionMonitor);
- NLM_CONNECTIVITY required = d->sameSubnet
- ? (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_SUBNET : NLM_CONNECTIVITY_IPV4_SUBNET)
- : (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET : NLM_CONNECTIVITY_IPV4_INTERNET);
- return d_func()->connectivity & required;
-}
-
-class QNetworkListManagerEvents : public INetworkListManagerEvents
-{
-public:
- QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor);
- virtual ~QNetworkListManagerEvents();
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
-
- ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
- ULONG STDMETHODCALLTYPE Release() override
- {
- if (--ref == 0) {
- delete this;
- return 0;
- }
- return ref;
- }
-
- HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override;
-
- Q_REQUIRED_RESULT
- bool start();
- Q_REQUIRED_RESULT
- bool stop();
-
-private:
- ComPtr<INetworkListManager> networkListManager = nullptr;
- ComPtr<IConnectionPoint> connectionPoint = nullptr;
-
- QNetworkStatusMonitorPrivate *monitor = nullptr;
-
- QAtomicInteger<ULONG> ref = 0;
- DWORD cookie = 0;
-};
-
-class QNetworkStatusMonitorPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QNetworkStatusMonitor);
-
-public:
- QNetworkStatusMonitorPrivate();
- ~QNetworkStatusMonitorPrivate();
-
- Q_REQUIRED_RESULT
- bool start();
- void stop();
-
- void setConnectivity(NLM_CONNECTIVITY newConnectivity);
-
-private:
- friend class QNetworkListManagerEvents;
-
- ComPtr<QNetworkListManagerEvents> managerEvents;
- NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED;
-
- bool monitoring = false;
- bool comInitFailed = false;
-};
-
-QNetworkListManagerEvents::QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor)
- : monitor(monitor)
-{
- auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
- IID_INetworkListManager, &networkListManager);
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:"
- << errorStringFromHResult(hr);
- return;
- }
-
- // Set initial connectivity
- hr = networkListManager->GetConnectivity(&monitor->connectivity);
- if (FAILED(hr))
- qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr);
-
- ComPtr<IConnectionPointContainer> connectionPointContainer;
- hr = networkListManager.As(&connectionPointContainer);
- if (SUCCEEDED(hr)) {
- hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
- &connectionPoint);
- }
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to get connection point for network list manager events:"
- << errorStringFromHResult(hr);
- }
-}
-
-QNetworkListManagerEvents::~QNetworkListManagerEvents()
-{
- Q_ASSERT(ref == 0);
-}
-
-HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
-{
- if (!ppvObject)
- return E_INVALIDARG;
-
- return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
- || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
- ? S_OK
- : E_NOINTERFACE;
-}
-HRESULT STDMETHODCALLTYPE
-QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
-{
- // This function is run on a different thread than 'monitor' is created on, so we need to run
- // it on that thread
- QMetaObject::invokeMethod(monitor->q_ptr,
- [newConnectivity, monitor = this->monitor]() {
- monitor->setConnectivity(newConnectivity);
- },
- Qt::QueuedConnection);
- return S_OK;
-}
-
-bool QNetworkListManagerEvents::start()
-{
- if (!connectionPoint) {
- qCWarning(lcNetMon, "Initialization failed, can't start!");
- return false;
- }
- auto hr = connectionPoint->Advise(this, &cookie);
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:"
- << errorStringFromHResult(hr);
- return false;
- }
- return true;
-}
-
-bool QNetworkListManagerEvents::stop()
-{
- Q_ASSERT(connectionPoint);
- auto hr = connectionPoint->Unadvise(cookie);
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to unsubscribe from network connectivity events:"
- << errorStringFromHResult(hr);
- return false;
- }
- cookie = 0;
- return true;
-}
-
-QNetworkStatusMonitorPrivate::QNetworkStatusMonitorPrivate()
-{
- auto hr = CoInitialize(nullptr);
- if (FAILED(hr)) {
- qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
- comInitFailed = true;
- return;
- }
- managerEvents = new QNetworkListManagerEvents(this);
-}
-
-QNetworkStatusMonitorPrivate::~QNetworkStatusMonitorPrivate()
-{
- if (comInitFailed)
- return;
- if (monitoring)
- stop();
- managerEvents.Reset();
- CoUninitialize();
-}
-
-void QNetworkStatusMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity)
-{
- Q_Q(QNetworkStatusMonitor);
-
- const bool oldAccessibility = q->isNetworkAccessible();
- connectivity = newConnectivity;
- const bool accessibility = q->isNetworkAccessible();
- if (oldAccessibility != accessibility)
- emit q->onlineStateChanged(accessibility);
-}
-
-bool QNetworkStatusMonitorPrivate::start()
-{
- if (comInitFailed)
- return false;
- Q_ASSERT(managerEvents);
- Q_ASSERT(!monitoring);
- if (managerEvents->start())
- monitoring = true;
- return monitoring;
-}
-
-void QNetworkStatusMonitorPrivate::stop()
-{
- Q_ASSERT(managerEvents);
- Q_ASSERT(monitoring);
- if (managerEvents->stop())
- monitoring = false;
-}
-
-QNetworkStatusMonitor::QNetworkStatusMonitor() : QObject(*new QNetworkStatusMonitorPrivate) {}
-
-QNetworkStatusMonitor::~QNetworkStatusMonitor() {}
-
-bool QNetworkStatusMonitor::start()
-{
- if (isMonitoring()) {
- qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
- return false;
- }
-
- return d_func()->start();
-}
-
-void QNetworkStatusMonitor::stop()
-{
- if (!isMonitoring()) {
- qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!");
- return;
+ const NLM_CONNECTIVITY RequiredSameSubnetIPv6 =
+ NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV6_SUBNET | NLM_CONNECTIVITY_IPV6_LOCALNETWORK
+ | NLM_CONNECTIVITY_IPV6_INTERNET);
+ const NLM_CONNECTIVITY RequiredSameSubnetIPv4 =
+ NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV4_LOCALNETWORK
+ | NLM_CONNECTIVITY_IPV4_INTERNET);
+
+ NLM_CONNECTIVITY required;
+ if (d->isLinkLocal) {
+ required = NLM_CONNECTIVITY(
+ d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_NOTRAFFIC | RequiredSameSubnetIPv6
+ : NLM_CONNECTIVITY_IPV4_NOTRAFFIC | RequiredSameSubnetIPv4);
+ } else if (d->sameSubnet) {
+ required =
+ NLM_CONNECTIVITY(d->remoteIsIPv6 ? RequiredSameSubnetIPv6 : RequiredSameSubnetIPv4);
+
+ } else {
+ required = NLM_CONNECTIVITY(d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET
+ : NLM_CONNECTIVITY_IPV4_INTERNET);
}
- d_func()->stop();
-}
-
-bool QNetworkStatusMonitor::isMonitoring() const
-{
- return d_func()->monitoring;
-}
-
-bool QNetworkStatusMonitor::isNetworkAccessible()
-{
- return d_func()->connectivity
- & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET
- | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET);
+ return d_func()->connectivity & required;
}
-bool QNetworkStatusMonitor::isEnabled()
+bool QNetworkConnectionMonitor::isEnabled()
{
return true;
}
-void QNetworkStatusMonitor::reachabilityChanged(bool online)
-{
- Q_UNUSED(online);
- Q_UNREACHABLE();
-}
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp
index c8c87d4549..a97eb25a51 100644
--- a/src/network/kernel/qnetworkdatagram.cpp
+++ b/src/network/kernel/qnetworkdatagram.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkdatagram.h"
#include "qnetworkdatagram_p.h"
@@ -44,6 +8,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QNetworkDatagram)
+
/*!
\class QNetworkDatagram
\brief The QNetworkDatagram class provides the data and metadata of a UDP datagram.
@@ -515,7 +481,7 @@ void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data)
void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
{
- Q_ASSUME(d);
+ Q_ASSERT(d);
delete d;
}
diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h
index 70958fea42..dcc2f1102f 100644
--- a/src/network/kernel/qnetworkdatagram.h
+++ b/src/network/kernel/qnetworkdatagram.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDATAGRAM_H
#define QNETWORKDATAGRAM_H
@@ -68,7 +32,7 @@ public:
{ swap(other); return *this; }
void swap(QNetworkDatagram &other) noexcept
- { qSwap(d, other.d); }
+ { qt_ptr_swap(d, other.d); }
void clear();
bool isValid() const;
@@ -91,7 +55,7 @@ public:
QByteArray data() const;
void setData(const QByteArray &data);
-#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_CLANG_QDOC)
+#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_QDOC)
QNetworkDatagram makeReply(const QByteArray &payload) const &
{ return makeReply_helper(payload); }
QNetworkDatagram makeReply(const QByteArray &payload) &&
@@ -116,7 +80,7 @@ Q_DECLARE_SHARED(QNetworkDatagram)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkDatagram)
+QT_DECL_METATYPE_EXTERN(QNetworkDatagram, Q_NETWORK_EXPORT)
#endif // QT_NO_UDPSOCKET
diff --git a/src/network/kernel/qnetworkdatagram_p.h b/src/network/kernel/qnetworkdatagram_p.h
index 5b5c037488..dcaddff1a9 100644
--- a/src/network/kernel/qnetworkdatagram_p.h
+++ b/src/network/kernel/qnetworkdatagram_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDATAGRAM_P_H
#define QNETWORKDATAGRAM_P_H
diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp
new file mode 100644
index 0000000000..10d6b89e2c
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation.cpp
@@ -0,0 +1,774 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// #define DEBUG_LOADING
+
+#include "qnetworkinformation.h"
+#include <QtNetwork/private/qnetworkinformation_p.h>
+#include <QtNetwork/qnetworkinformation.h>
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qthread.h>
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcNetInfo)
+Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info");
+
+struct QNetworkInformationDeleter
+{
+ void operator()(QNetworkInformation *information) { delete information; }
+};
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader,
+ (QNetworkInformationBackendFactory_iid,
+ QStringLiteral("/networkinformation")))
+
+struct QStaticNetworkInformationDataHolder
+{
+ QMutex instanceMutex;
+ std::unique_ptr<QNetworkInformation, QNetworkInformationDeleter> instanceHolder;
+ QList<QNetworkInformationBackendFactory *> factories;
+};
+Q_GLOBAL_STATIC(QStaticNetworkInformationDataHolder, dataHolder);
+
+static void networkInfoCleanup()
+{
+ if (!dataHolder.exists())
+ return;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ QNetworkInformation *instance = dataHolder->instanceHolder.get();
+ if (!instance)
+ return;
+
+ dataHolder->instanceHolder.reset();
+}
+
+using namespace Qt::Literals::StringLiterals;
+
+class QNetworkInformationDummyBackend : public QNetworkInformationBackend {
+ Q_OBJECT
+public:
+ QString name() const override { return u"dummy"_s; }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return {};
+ }
+};
+
+class QNetworkInformationPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QNetworkInformation)
+public:
+ QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) {
+ qAddPostRoutine(&networkInfoCleanup);
+ }
+
+ static QNetworkInformation *create(QNetworkInformation::Features features);
+ static QNetworkInformation *create(QStringView name);
+ static QNetworkInformation *createDummy();
+ static QNetworkInformation *instance()
+ {
+ if (!dataHolder())
+ return nullptr;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ return dataHolder->instanceHolder.get();
+ }
+ static QStringList backendNames();
+ static void addToList(QNetworkInformationBackendFactory *factory);
+ static void removeFromList(QNetworkInformationBackendFactory *factory);
+
+private:
+ static bool initializeList();
+
+ std::unique_ptr<QNetworkInformationBackend> backend;
+};
+
+bool QNetworkInformationPrivate::initializeList()
+{
+ if (!qniLoader())
+ return false;
+ if (!dataHolder())
+ return false;
+ Q_CONSTINIT static QBasicMutex mutex;
+ QMutexLocker initLocker(&mutex);
+
+#if QT_CONFIG(library)
+ qniLoader->update();
+#endif
+ // Instantiates the plugins (and registers the factories)
+ int index = 0;
+ while (qniLoader->instance(index))
+ ++index;
+ initLocker.unlock();
+
+ // Now sort the list on number of features available (then name)
+ const auto featuresNameOrder = [](QNetworkInformationBackendFactory *a,
+ QNetworkInformationBackendFactory *b) {
+ if (!a || !b)
+ return a && !b;
+ auto aFeaturesSupported = qPopulationCount(unsigned(a->featuresSupported()));
+ auto bFeaturesSupported = qPopulationCount(unsigned(b->featuresSupported()));
+ return aFeaturesSupported > bFeaturesSupported
+ || (aFeaturesSupported == bFeaturesSupported
+ && a->name().compare(b->name(), Qt::CaseInsensitive) < 0);
+ };
+ QMutexLocker instanceLocker(&dataHolder->instanceMutex);
+ std::sort(dataHolder->factories.begin(), dataHolder->factories.end(), featuresNameOrder);
+
+ return !dataHolder->factories.isEmpty();
+}
+
+void QNetworkInformationPrivate::addToList(QNetworkInformationBackendFactory *factory)
+{
+ // @note: factory is in the base class ctor
+ if (!dataHolder())
+ return;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ dataHolder->factories.append(factory);
+}
+
+void QNetworkInformationPrivate::removeFromList(QNetworkInformationBackendFactory *factory)
+{
+ // @note: factory is in the base class dtor
+ if (!dataHolder.exists())
+ return;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ dataHolder->factories.removeAll(factory);
+}
+
+QStringList QNetworkInformationPrivate::backendNames()
+{
+ if (!dataHolder())
+ return {};
+ if (!initializeList())
+ return {};
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ const QList copy = dataHolder->factories;
+ locker.unlock();
+
+ QStringList result;
+ result.reserve(copy.size());
+ for (const auto *factory : copy)
+ result << factory->name();
+ return result;
+}
+
+QNetworkInformation *QNetworkInformationPrivate::create(QStringView name)
+{
+ if (name.isEmpty())
+ return nullptr;
+ if (!dataHolder())
+ return nullptr;
+#ifdef DEBUG_LOADING
+ qDebug().nospace() << "create() called with name=\"" << name
+ << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
+#endif
+ if (!initializeList()) {
+#ifdef DEBUG_LOADING
+ qDebug("Failed to initialize list, returning.");
+#endif
+ return nullptr;
+ }
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+
+ const auto nameMatches = [name](QNetworkInformationBackendFactory *factory) {
+ return factory->name().compare(name, Qt::CaseInsensitive) == 0;
+ };
+ auto it = std::find_if(dataHolder->factories.cbegin(), dataHolder->factories.cend(),
+ nameMatches);
+ if (it == dataHolder->factories.cend()) {
+#ifdef DEBUG_LOADING
+ if (dataHolder->factories.isEmpty()) {
+ qDebug("No plugins available");
+ } else {
+ QString listNames;
+ listNames.reserve(8 * dataHolder->factories.count());
+ for (const auto *factory : std::as_const(dataHolder->factories))
+ listNames += factory->name() + ", "_L1;
+ listNames.chop(2);
+ qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
+ << listNames << " }";
+ }
+#endif
+ return nullptr;
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Creating instance using loader named " << (*it)->name();
+#endif
+ QNetworkInformationBackend *backend = (*it)->create((*it)->featuresSupported());
+ if (!backend)
+ return nullptr;
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ Q_ASSERT(name.isEmpty()
+ || dataHolder->instanceHolder->backendName().compare(name, Qt::CaseInsensitive) == 0);
+ return dataHolder->instanceHolder.get();
+}
+
+QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Features features)
+{
+ if (!dataHolder())
+ return nullptr;
+#ifdef DEBUG_LOADING
+ qDebug().nospace() << "create() called with features=\"" << features
+ << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
+#endif
+ if (features == 0)
+ return nullptr;
+
+ if (!initializeList()) {
+#ifdef DEBUG_LOADING
+ qDebug("Failed to initialize list, returning.");
+#endif
+ return nullptr;
+ }
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+ const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
+ return factory && factory->featuresSupported().testFlags(features);
+ };
+
+ for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
+ ++it) {
+ it = std::find_if(it, end, supportsRequestedFeatures);
+ if (it == end) {
+#ifdef DEBUG_LOADING
+ if (dataHolder->factories.isEmpty()) {
+ qDebug("No plugins available");
+ } else {
+ QStringList names;
+ names.reserve(dataHolder->factories.count());
+ for (const auto *factory : std::as_const(dataHolder->factories))
+ names += factory->name();
+ qDebug() << "None of the following backends has all the requested features:"
+ << names << features;
+ }
+#endif
+ break;
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Creating instance using loader named" << (*it)->name();
+#endif
+ if (QNetworkInformationBackend *backend = (*it)->create(features)) {
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ Q_ASSERT(dataHolder->instanceHolder->supports(features));
+ return dataHolder->instanceHolder.get();
+ }
+#ifdef DEBUG_LOADING
+ else {
+ qDebug() << "The factory returned a nullptr";
+ }
+#endif
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Couldn't find/create an appropriate backend.";
+#endif
+ return nullptr;
+}
+
+QNetworkInformation *QNetworkInformationPrivate::createDummy()
+{
+ if (!dataHolder())
+ return nullptr;
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+ QNetworkInformationBackend *backend = new QNetworkInformationDummyBackend;
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ return dataHolder->instanceHolder.get();
+}
+
+/*!
+ \class QNetworkInformationBackend
+ \internal (Semi-private)
+ \brief QNetworkInformationBackend provides the interface with
+ which QNetworkInformation does all of its actual work.
+
+ Deriving from and implementing this class makes it a candidate
+ for use with QNetworkInformation. The derived class must, on
+ updates, call setters in the QNetworkInformationBackend which
+ will update the values and emit signals if the value has changed.
+
+ \sa QNetworkInformationBackendFactory
+*/
+
+/*!
+ \internal
+ Destroys base backend class.
+*/
+QNetworkInformationBackend::~QNetworkInformationBackend() = default;
+
+/*!
+ \fn QNetworkInformationBackend::name()
+
+ Backend name, return the same in
+ QNetworkInformationBackendFactory::name().
+*/
+
+/*!
+ \fn QNetworkInformation::Features QNetworkInformationBackend::featuresSupported()
+
+ Features supported, return the same in
+ QNetworkInformationBackendFactory::featuresSupported().
+*/
+
+/*!
+ \fn void QNetworkInformationBackend::reachabilityChanged()
+
+ You should not emit this signal manually, call setReachability()
+ instead which will emit this signal when the value changes.
+
+ \sa setReachability
+*/
+
+/*!
+ \fn void QNetworkInformationBackend::setReachability(QNetworkInformation::Reachability reachability)
+
+ Call this when reachability has changed. It will automatically
+ emit reachabilityChanged().
+
+ \sa setReachability
+*/
+
+/*!
+ \class QNetworkInformationBackendFactory
+ \internal (Semi-private)
+ \brief QNetworkInformationBackendFactory provides the interface
+ for creating instances of QNetworkInformationBackend.
+
+ Deriving from and implementing this class will let you register
+ your plugin with QNetworkInformation. It must provide some basic
+ information for querying information about the backend, and must
+ also create the backend if requested. If some pre-conditions for
+ the backend is not met it must return \nullptr.
+*/
+
+/*!
+ \internal
+ Adds the factory to an internal list.
+*/
+QNetworkInformationBackendFactory::QNetworkInformationBackendFactory()
+{
+ QNetworkInformationPrivate::addToList(this);
+}
+
+/*!
+ \internal
+ Removes the factory from an internal list.
+*/
+QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory()
+{
+ QNetworkInformationPrivate::removeFromList(this);
+}
+
+/*!
+ \fn QString QNetworkInformationBackendFactory::name()
+
+ Backend name, return the same in
+ QNetworkInformationBackend::name().
+*/
+
+/*!
+ \fn QNetworkInformation::Features QNetworkInformationBackendFactory::featuresSupported()
+
+ Features supported, return the same in
+ QNetworkInformationBackend::featuresSupported().
+ The factory should not promise support for features that wouldn't
+ be available after creating the backend.
+*/
+
+/*!
+ \fn QNetworkInformationBackend *QNetworkInformationBackendFactory::create()
+
+ Create and return an instance of QNetworkInformationBackend. It
+ will be deallocated by QNetworkInformation on shutdown. If some
+ precondition is not met, meaning the backend would not function
+ correctly, then you must return \nullptr.
+*/
+
+/*!
+ \class QNetworkInformation
+ \inmodule QtNetwork
+ \since 6.1
+ \brief QNetworkInformation exposes various network information
+ through native backends.
+
+ QNetworkInformation provides a cross-platform interface to
+ network-related information through plugins.
+
+ Various plugins can have various functionality supported, and so
+ you can load() plugins based on which features are needed.
+
+ QNetworkInformation is a singleton and stays alive from the first
+ successful load() until destruction of the QCoreApplication object.
+ If you destroy and re-create the QCoreApplication object you must call
+ load() again.
+
+ \sa QNetworkInformation::Feature
+*/
+
+/*!
+ \enum QNetworkInformation::Feature
+
+ Lists all of the features that a plugin may currently support.
+ This can be used in QNetworkInformation::load().
+
+ \value Reachability
+ If the plugin supports this feature then the \c reachability property
+ will provide useful results. Otherwise it will always return
+ \c{Reachability::Unknown}.
+ See also QNetworkInformation::Reachability.
+
+ \value CaptivePortal
+ If the plugin supports this feature then the \c isBehindCaptivePortal
+ property will provide useful results. Otherwise it will always return
+ \c{false}.
+
+ \value TransportMedium
+ If the plugin supports this feature then the \c transportMedium
+ property will provide useful results. Otherwise it will always return
+ \c{TransportMedium::Unknown}.
+ See also QNetworkInformation::TransportMedium.
+
+ \value Metered
+ If the plugin supports this feature then the \c isMetered
+ property will provide useful results. Otherwise it will always return
+ \c{false}.
+*/
+
+/*!
+ \enum QNetworkInformation::Reachability
+
+ \value Unknown
+ If this value is returned then we may be connected but the OS
+ has still not confirmed full connectivity, or this feature
+ is not supported.
+ \value Disconnected
+ Indicates that the system may have no connectivity at all.
+ \value Local
+ Indicates that the system is connected to a network, but it
+ might only be able to access devices on the local network.
+ \value Site
+ Indicates that the system is connected to a network, but it
+ might only be able to access devices on the local subnet or an
+ intranet.
+ \value Online
+ Indicates that the system is connected to a network and
+ able to access the Internet.
+
+ \sa QNetworkInformation::reachability
+*/
+
+/*!
+ \enum QNetworkInformation::TransportMedium
+ \since 6.3
+
+ Lists the currently recognized media with which one can connect to the
+ internet.
+
+ \value Unknown
+ Returned if either the OS reports no active medium, the active medium is
+ not recognized by Qt, or the TransportMedium feature is not supported.
+ \value Ethernet
+ Indicates that the currently active connection is using ethernet.
+ Note: This value may also be returned when Windows is connected to a
+ Bluetooth personal area network.
+ \value Cellular
+ Indicates that the currently active connection is using a cellular
+ network.
+ \value WiFi
+ Indicates that the currently active connection is using Wi-Fi.
+ \value Bluetooth
+ Indicates that the currently active connection is connected using
+ Bluetooth.
+
+ \sa QNetworkInformation::transportMedium
+*/
+
+/*!
+ \internal ctor
+*/
+QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
+ : QObject(*(new QNetworkInformationPrivate(backend)))
+{
+ connect(backend, &QNetworkInformationBackend::reachabilityChanged, this,
+ &QNetworkInformation::reachabilityChanged);
+ connect(backend, &QNetworkInformationBackend::behindCaptivePortalChanged, this,
+ &QNetworkInformation::isBehindCaptivePortalChanged);
+ connect(backend, &QNetworkInformationBackend::transportMediumChanged, this,
+ &QNetworkInformation::transportMediumChanged);
+ connect(backend, &QNetworkInformationBackend::isMeteredChanged, this,
+ &QNetworkInformation::isMeteredChanged);
+
+ QThread *main = nullptr;
+
+ if (QCoreApplication::instance())
+ main = QCoreApplication::instance()->thread();
+
+ if (main && thread() != main)
+ moveToThread(main);
+}
+
+/*!
+ \internal dtor
+*/
+QNetworkInformation::~QNetworkInformation() = default;
+
+/*!
+ \property QNetworkInformation::reachability
+ \brief The current state of the system's network connectivity.
+
+ Indicates the level of connectivity that can be expected. Do note
+ that this is only based on what the plugin/operating system
+ reports. In certain scenarios this is known to be wrong. For
+ example, on Windows the 'Online' check, by default, is performed
+ by Windows connecting to a Microsoft-owned server. If this server
+ is for any reason blocked then it will assume it does not have
+ Online reachability. Because of this you should not use this as a
+ pre-check before attempting to make a connection.
+*/
+
+QNetworkInformation::Reachability QNetworkInformation::reachability() const
+{
+ return d_func()->backend->reachability();
+}
+
+/*!
+ \property QNetworkInformation::isBehindCaptivePortal
+ \brief Lets you know if the user's device is behind a captive portal.
+ \since 6.2
+
+ This property indicates if the user's device is currently known to be
+ behind a captive portal. This functionality relies on the operating system's
+ detection of captive portals and is not supported on systems that don't
+ report this. On systems where this is not supported this will always return
+ \c{false}.
+*/
+bool QNetworkInformation::isBehindCaptivePortal() const
+{
+ return d_func()->backend->behindCaptivePortal();
+}
+
+/*!
+ \property QNetworkInformation::transportMedium
+ \brief The currently active transport medium for the application
+ \since 6.3
+
+ This property returns the currently active transport medium for the
+ application, on operating systems where such information is available.
+
+ When the current transport medium changes a signal is emitted, this can,
+ for instance, occur when a user leaves the range of a WiFi network, unplugs
+ their ethernet cable or enables Airplane mode.
+*/
+QNetworkInformation::TransportMedium QNetworkInformation::transportMedium() const
+{
+ return d_func()->backend->transportMedium();
+}
+
+/*!
+ \property QNetworkInformation::isMetered
+ \brief Check if the current connection is metered
+ \since 6.3
+
+ This property returns whether the current connection is (known to be)
+ metered or not. You can use this as a guiding factor to decide whether your
+ application should perform certain network requests or uploads.
+ For instance, you may not want to upload logs or diagnostics while this
+ property is \c true.
+*/
+bool QNetworkInformation::isMetered() const
+{
+ return d_func()->backend->isMetered();
+}
+
+/*!
+ Returns the name of the currently loaded backend.
+*/
+QString QNetworkInformation::backendName() const
+{
+ return d_func()->backend->name();
+}
+
+/*!
+ Returns \c true if the currently loaded backend supports
+ \a features.
+*/
+bool QNetworkInformation::supports(Features features) const
+{
+ return (d_func()->backend->featuresSupported() & features) == features;
+}
+
+/*!
+ \since 6.3
+
+ Returns all the supported features of the current backend.
+*/
+QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
+{
+ return d_func()->backend->featuresSupported();
+}
+
+/*!
+ \since 6.3
+
+ Attempts to load the platform-default backend.
+
+ \note Starting with 6.7 this tries to load any backend that supports
+ \l{QNetworkInformation::Feature::Reachability}{Reachability} if the
+ platform-default backend is not available or fails to load.
+ If this also fails it will fall back to a backend that only returns
+ the default values for all properties.
+
+ This platform-to-plugin mapping is as follows:
+
+ \table
+ \header
+ \li Platform
+ \li Plugin-name
+ \row
+ \li Windows
+ \li networklistmanager
+ \row
+ \li Apple (macOS/iOS)
+ \li scnetworkreachability
+ \row
+ \li Android
+ \li android
+ \row
+ \li Linux
+ \li networkmanager
+ \endtable
+
+ This function is provided for convenience where the logic earlier
+ is good enough. If you require a specific plugin then you should call
+ loadBackendByName() or loadBackendByFeatures() directly instead.
+
+ Determines a suitable backend to load and returns \c true if this backend
+ is already loaded or on successful loading of it. Returns \c false if any
+ other backend has already been loaded, or if loading of the selected
+ backend fails.
+
+ \sa instance(), load()
+*/
+bool QNetworkInformation::loadDefaultBackend()
+{
+ int index = -1;
+#ifdef Q_OS_WIN
+ index = QNetworkInformationBackend::PluginNamesWindowsIndex;
+#elif defined(Q_OS_DARWIN)
+ index = QNetworkInformationBackend::PluginNamesAppleIndex;
+#elif defined(Q_OS_ANDROID)
+ index = QNetworkInformationBackend::PluginNamesAndroidIndex;
+#elif defined(Q_OS_LINUX)
+ index = QNetworkInformationBackend::PluginNamesLinuxIndex;
+#endif
+ if (index != -1 && loadBackendByName(QNetworkInformationBackend::PluginNames[index]))
+ return true;
+ // We assume reachability is the most commonly wanted feature, and try to
+ // load the backend that advertises the most features including that:
+ if (loadBackendByFeatures(Feature::Reachability))
+ return true;
+
+ // Fall back to the dummy backend
+ return loadBackendByName(u"dummy");
+}
+
+/*!
+ \since 6.4
+
+ Attempts to load a backend whose name matches \a backend
+ (case insensitively).
+
+ Returns \c true if it managed to load the requested backend or
+ if it was already loaded. Returns \c false otherwise.
+
+ \sa instance
+*/
+bool QNetworkInformation::loadBackendByName(QStringView backend)
+{
+ if (backend == u"dummy")
+ return QNetworkInformationPrivate::createDummy() != nullptr;
+
+ auto loadedBackend = QNetworkInformationPrivate::create(backend);
+ return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0;
+}
+
+#if QT_DEPRECATED_SINCE(6,4)
+/*!
+ \deprecated [6.4] Use loadBackendByName() instead.
+
+ \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures()
+*/
+bool QNetworkInformation::load(QStringView backend)
+{
+ return loadBackendByName(backend);
+}
+#endif // QT_DEPRECATED_SINCE(6,4)
+
+/*!
+ \since 6.4
+ Load a backend which supports \a features.
+
+ Returns \c true if it managed to load the requested backend or
+ if it was already loaded. Returns \c false otherwise.
+
+ \sa instance
+*/
+bool QNetworkInformation::loadBackendByFeatures(Features features)
+{
+ auto loadedBackend = QNetworkInformationPrivate::create(features);
+ return loadedBackend && loadedBackend->supports(features);
+}
+
+#if QT_DEPRECATED_SINCE(6,4)
+/*!
+ \deprecated [6.4] Use loadBackendByFeatures() instead.
+
+ \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures()
+*/
+bool QNetworkInformation::load(Features features)
+{
+ return loadBackendByFeatures(features);
+}
+#endif // QT_DEPRECATED_SINCE(6,4)
+
+/*!
+ Returns a list of the names of all currently available backends.
+*/
+QStringList QNetworkInformation::availableBackends()
+{
+ return QNetworkInformationPrivate::backendNames();
+}
+
+/*!
+ Returns a pointer to the instance of the QNetworkInformation,
+ if any.
+
+ \sa load()
+*/
+QNetworkInformation *QNetworkInformation::instance()
+{
+ return QNetworkInformationPrivate::instance();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qnetworkinformation.cpp"
+#include "moc_qnetworkinformation_p.cpp"
+#include "qnetworkinformation.moc"
diff --git a/src/network/kernel/qnetworkinformation.h b/src/network/kernel/qnetworkinformation.h
new file mode 100644
index 0000000000..57a49f23c8
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKINFORMATION_H
+#define QNETWORKINFORMATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstringview.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkInformationBackend;
+class QNetworkInformationPrivate;
+struct QNetworkInformationDeleter;
+class Q_NETWORK_EXPORT QNetworkInformation : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QNetworkInformation)
+ Q_PROPERTY(Reachability reachability READ reachability NOTIFY reachabilityChanged)
+ Q_PROPERTY(bool isBehindCaptivePortal READ isBehindCaptivePortal
+ NOTIFY isBehindCaptivePortalChanged)
+ Q_PROPERTY(TransportMedium transportMedium READ transportMedium NOTIFY transportMediumChanged)
+ Q_PROPERTY(bool isMetered READ isMetered NOTIFY isMeteredChanged)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+public:
+ enum class Reachability {
+ Unknown,
+ Disconnected,
+ Local,
+ Site,
+ Online,
+ };
+ Q_ENUM(Reachability)
+
+ enum class TransportMedium {
+ Unknown,
+ Ethernet,
+ Cellular,
+ WiFi,
+ Bluetooth,
+ };
+ Q_ENUM(TransportMedium)
+
+ enum class Feature {
+ Reachability = 0x1,
+ CaptivePortal = 0x2,
+ TransportMedium = 0x4,
+ Metered = 0x8,
+ };
+ Q_DECLARE_FLAGS(Features, Feature)
+ Q_FLAG(Features)
+
+ Reachability reachability() const;
+
+ bool isBehindCaptivePortal() const;
+
+ TransportMedium transportMedium() const;
+
+ bool isMetered() const;
+
+ QString backendName() const;
+
+ bool supports(Features features) const;
+ Features supportedFeatures() const;
+
+ static bool loadDefaultBackend();
+ static bool loadBackendByName(QStringView backend);
+ static bool loadBackendByFeatures(Features features);
+#if QT_DEPRECATED_SINCE(6,4)
+ QT_DEPRECATED_VERSION_X_6_4("Use loadBackendByName") static bool load(QStringView backend);
+ QT_DEPRECATED_VERSION_X_6_4("Use loadBackendByFeatures") static bool load(Features features);
+#endif
+ static QStringList availableBackends();
+ static QNetworkInformation *instance();
+
+Q_SIGNALS:
+ void reachabilityChanged(Reachability newReachability);
+ void isBehindCaptivePortalChanged(bool state);
+ void transportMediumChanged(TransportMedium current);
+ void isMeteredChanged(bool isMetered);
+
+private:
+ friend struct QNetworkInformationDeleter;
+ QNetworkInformation(QNetworkInformationBackend *backend);
+ ~QNetworkInformation() override;
+
+ Q_DISABLE_COPY_MOVE(QNetworkInformation)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkInformation::Features)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkinformation_p.h b/src/network/kernel/qnetworkinformation_p.h
new file mode 100644
index 0000000000..504955a6e1
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation_p.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKINFORMATION_P_H
+#define QNETWORKINFORMATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Information API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qnetworkinformation.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qreadwritelock.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_NETWORK_EXPORT QNetworkInformationBackend : public QObject
+{
+ Q_OBJECT
+
+ using Reachability = QNetworkInformation::Reachability;
+ using TransportMedium = QNetworkInformation::TransportMedium;
+
+public:
+ static inline const char16_t PluginNames[4][22] = {
+ { u"networklistmanager" },
+ { u"scnetworkreachability" },
+ { u"android" },
+ { u"networkmanager" },
+ };
+ static constexpr int PluginNamesWindowsIndex = 0;
+ static constexpr int PluginNamesAppleIndex = 1;
+ static constexpr int PluginNamesAndroidIndex = 2;
+ static constexpr int PluginNamesLinuxIndex = 3;
+
+ QNetworkInformationBackend() = default;
+ ~QNetworkInformationBackend() override;
+
+ virtual QString name() const = 0;
+ virtual QNetworkInformation::Features featuresSupported() const = 0;
+
+ Reachability reachability() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_reachability;
+ }
+
+ bool behindCaptivePortal() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_behindCaptivePortal;
+ }
+
+ TransportMedium transportMedium() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_transportMedium;
+ }
+
+ bool isMetered() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_metered;
+ }
+
+Q_SIGNALS:
+ void reachabilityChanged(Reachability reachability);
+ void behindCaptivePortalChanged(bool behindPortal);
+ void transportMediumChanged(TransportMedium medium);
+ void isMeteredChanged(bool isMetered);
+
+protected:
+ void setReachability(QNetworkInformation::Reachability reachability)
+ {
+ QWriteLocker locker(&m_lock);
+ if (m_reachability != reachability) {
+ m_reachability = reachability;
+ locker.unlock();
+ emit reachabilityChanged(reachability);
+ }
+ }
+
+ void setBehindCaptivePortal(bool behindPortal)
+ {
+ QWriteLocker locker(&m_lock);
+ if (m_behindCaptivePortal != behindPortal) {
+ m_behindCaptivePortal = behindPortal;
+ locker.unlock();
+ emit behindCaptivePortalChanged(behindPortal);
+ }
+ }
+
+ void setTransportMedium(TransportMedium medium)
+ {
+ QWriteLocker locker(&m_lock);
+ if (m_transportMedium != medium) {
+ m_transportMedium = medium;
+ locker.unlock();
+ emit transportMediumChanged(medium);
+ }
+ }
+
+ void setMetered(bool isMetered)
+ {
+ QWriteLocker locker(&m_lock);
+ if (m_metered != isMetered) {
+ m_metered = isMetered;
+ locker.unlock();
+ emit isMeteredChanged(isMetered);
+ }
+ }
+
+private:
+ mutable QReadWriteLock m_lock;
+ Reachability m_reachability = Reachability::Unknown;
+ TransportMedium m_transportMedium = TransportMedium::Unknown;
+ bool m_behindCaptivePortal = false;
+ bool m_metered = false;
+
+ Q_DISABLE_COPY_MOVE(QNetworkInformationBackend)
+ friend class QNetworkInformation;
+ friend class QNetworkInformationPrivate;
+};
+
+class Q_NETWORK_EXPORT QNetworkInformationBackendFactory : public QObject
+{
+ Q_OBJECT
+
+ using Features = QNetworkInformation::Features;
+
+public:
+ QNetworkInformationBackendFactory();
+ virtual ~QNetworkInformationBackendFactory();
+ virtual QString name() const = 0;
+ virtual QNetworkInformationBackend *create(Features requiredFeatures) const = 0;
+ virtual Features featuresSupported() const = 0;
+
+private:
+ Q_DISABLE_COPY_MOVE(QNetworkInformationBackendFactory)
+};
+#define QNetworkInformationBackendFactory_iid "org.qt-project.Qt.NetworkInformationBackendFactory"
+Q_DECLARE_INTERFACE(QNetworkInformationBackendFactory, QNetworkInformationBackendFactory_iid);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index eed57f8a32..f03e85c7f6 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2017 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2017 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkinterface.h"
#include "qnetworkinterface_p.h"
@@ -49,6 +13,12 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QNetworkAddressEntry)
+QT_IMPL_METATYPE_EXTERN(QNetworkInterface)
+
+static_assert(QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ && sizeof(QScopedPointer<QNetworkAddressEntryPrivate>) == sizeof(std::unique_ptr<QNetworkAddressEntryPrivate>));
+
static QList<QNetworkInterfacePrivate *> postProcess(QList<QNetworkInterfacePrivate *> list)
{
// Some platforms report a netmask but don't report a broadcast address
@@ -139,7 +109,7 @@ QString QNetworkInterfacePrivate::makeHwAddress(int len, uchar *data)
QChar *out = result.data();
for (int i = 0; i < len; ++i) {
if (i)
- *out++ = QLatin1Char(':');
+ *out++ = u':';
*out++ = QLatin1Char(QtMiscUtils::toHexUpper(data[i] / 16));
*out++ = QLatin1Char(QtMiscUtils::toHexUpper(data[i] % 16));
}
@@ -204,7 +174,7 @@ QNetworkAddressEntry::QNetworkAddressEntry()
object \a other.
*/
QNetworkAddressEntry::QNetworkAddressEntry(const QNetworkAddressEntry &other)
- : d(new QNetworkAddressEntryPrivate(*other.d.data()))
+ : d(new QNetworkAddressEntryPrivate(*other.d.get()))
{
}
@@ -213,7 +183,7 @@ QNetworkAddressEntry::QNetworkAddressEntry(const QNetworkAddressEntry &other)
*/
QNetworkAddressEntry &QNetworkAddressEntry::operator=(const QNetworkAddressEntry &other)
{
- *d.data() = *other.d.data();
+ *d.get() = *other.d.get();
return *this;
}
@@ -489,7 +459,7 @@ void QNetworkAddressEntry::clearAddressLifetime()
\since 5.11
Returns \c true if this address is permanent on this interface, \c false if
- it's temporary. A permenant address is one which has no expiration time and
+ it's temporary. A permanent address is one which has no expiration time and
is often static (manually configured).
If this information could not be determined, this function returns \c true.
@@ -552,9 +522,11 @@ bool QNetworkAddressEntry::isPermanent() const
Specifies the flags associated with this network interface. The
possible values are:
- \value IsUp the network interface is active
- \value IsRunning the network interface has resources
- allocated
+ \value IsUp the network interface is "up" -
+ enabled by administrative action
+ \value IsRunning the network interface is operational:
+ configured "up" and (typically)
+ physically connected to a network
\value CanBroadcast the network interface works in
broadcast mode
\value IsLoopBack the network interface is a loopback
@@ -901,7 +873,7 @@ QList<QHostAddress> QNetworkInterface::allAddresses()
if ((p->flags & QNetworkInterface::IsUp) == 0)
continue;
- for (const QNetworkAddressEntry &entry : qAsConst(p->addressEntries))
+ for (const QNetworkAddressEntry &entry : std::as_const(p->addressEntries))
result += entry.ip();
}
@@ -926,17 +898,32 @@ static inline QDebug flagsDebug(QDebug debug, QNetworkInterface::InterfaceFlags
return debug;
}
-static inline QDebug operator<<(QDebug debug, const QNetworkAddressEntry &entry)
+/*!
+ \since 6.2
+
+ Writes the QNetworkAddressEntry \a entry to the stream and
+ returns a reference to the \a debug stream.
+
+ \relates QNetworkAddressEntry
+ */
+QDebug operator<<(QDebug debug, const QNetworkAddressEntry &entry)
{
- debug << "(address = " << entry.ip();
+ QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+ debug << "address = " << entry.ip();
if (!entry.netmask().isNull())
debug << ", netmask = " << entry.netmask();
if (!entry.broadcast().isNull())
debug << ", broadcast = " << entry.broadcast();
- debug << ')';
return debug;
}
+/*!
+ Writes the QNetworkInterface \a networkInterface to the stream and
+ returns a reference to the \a debug stream.
+
+ \relates QNetworkInterface
+ */
QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface)
{
QDebugStateSaver saver(debug);
diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h
index 4caedaa38f..2d79f9e2a1 100644
--- a/src/network/kernel/qnetworkinterface.h
+++ b/src/network/kernel/qnetworkinterface.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKINTERFACE_H
#define QNETWORKINTERFACE_H
@@ -45,12 +9,13 @@
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qhostaddress.h>
+#include <memory>
+
#ifndef QT_NO_NETWORKINTERFACE
QT_BEGIN_NAMESPACE
class QDeadlineTimer;
-template<typename T> class QList;
class QNetworkAddressEntryPrivate;
class Q_NETWORK_EXPORT QNetworkAddressEntry
@@ -68,7 +33,7 @@ public:
QNetworkAddressEntry &operator=(const QNetworkAddressEntry &other);
~QNetworkAddressEntry();
- void swap(QNetworkAddressEntry &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkAddressEntry &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkAddressEntry &other) const;
inline bool operator!=(const QNetworkAddressEntry &other) const
@@ -97,7 +62,8 @@ public:
bool isTemporary() const { return !isPermanent(); }
private:
- QScopedPointer<QNetworkAddressEntryPrivate> d;
+ // ### Qt 7: make implicitly shared
+ std::unique_ptr<QNetworkAddressEntryPrivate> d;
};
Q_DECLARE_SHARED(QNetworkAddressEntry)
@@ -144,7 +110,7 @@ public:
QNetworkInterface &operator=(const QNetworkInterface &other);
~QNetworkInterface();
- void swap(QNetworkInterface &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkInterface &other) noexcept { d.swap(other.d); }
bool isValid() const;
@@ -174,13 +140,14 @@ Q_DECLARE_SHARED(QNetworkInterface)
Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkInterface::InterfaceFlags)
#ifndef QT_NO_DEBUG_STREAM
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkAddressEntry &entry);
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface);
#endif
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkAddressEntry)
-Q_DECLARE_METATYPE(QNetworkInterface)
+QT_DECL_METATYPE_EXTERN(QNetworkAddressEntry, Q_NETWORK_EXPORT)
+QT_DECL_METATYPE_EXTERN(QNetworkInterface, Q_NETWORK_EXPORT)
#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkinterface_linux.cpp b/src/network/kernel/qnetworkinterface_linux.cpp
index 25aba5836e..67ed8006bb 100644
--- a/src/network/kernel/qnetworkinterface_linux.cpp
+++ b/src/network/kernel/qnetworkinterface_linux.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkinterface.h"
#include "qnetworkinterface_p.h"
@@ -47,7 +11,7 @@
#include <qobjectdefs.h>
#include <qvarlengtharray.h>
-// accordding to rtnetlink(7)
+// according to rtnetlink(7)
#include <asm/types.h>
#include <linux/if.h>
#include <linux/if_arp.h>
@@ -147,12 +111,15 @@ struct NetlinkSocket
template <typename Lambda> struct ProcessNetlinkRequest
{
using FunctionTraits = QtPrivate::FunctionPointer<decltype(&Lambda::operator())>;
- using FirstArgument = typename FunctionTraits::Arguments::Car;
+ using FirstArgumentPointer = typename FunctionTraits::Arguments::Car;
+ using FirstArgument = std::remove_pointer_t<FirstArgumentPointer>;
+ static_assert(std::is_pointer_v<FirstArgumentPointer>);
+ static_assert(std::is_aggregate_v<FirstArgument>);
static int expectedTypeForRequest(int rtype)
{
- Q_STATIC_ASSERT(RTM_NEWADDR == RTM_GETADDR - 2);
- Q_STATIC_ASSERT(RTM_NEWLINK == RTM_GETLINK - 2);
+ static_assert(RTM_NEWADDR == RTM_GETADDR - 2);
+ static_assert(RTM_NEWLINK == RTM_GETLINK - 2);
Q_ASSERT(rtype == RTM_GETADDR || rtype == RTM_GETLINK);
return rtype - 2;
}
@@ -172,7 +139,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
if (!NLMSG_OK(hdr, quint32(len)))
return;
- auto arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ auto arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
size_t payloadLen = NLMSG_PAYLOAD(hdr, 0);
// is this a multipart message?
@@ -192,7 +159,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
// NLMSG_NEXT also updates the len variable
hdr = NLMSG_NEXT(hdr, len);
- arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
payloadLen = NLMSG_PAYLOAD(hdr, 0);
} while (NLMSG_OK(hdr, quint32(len)));
@@ -222,7 +189,7 @@ void processNetlinkRequest(int sock, struct nlmsghdr *hdr, char *buf, size_t buf
uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
{
uint index = 0;
- if (name.length() >= IFNAMSIZ)
+ if (name.size() >= IFNAMSIZ)
return index;
int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
@@ -303,9 +270,9 @@ static QList<QNetworkInterfacePrivate *> getInterfaces(int sock, char *buf)
case IFLA_OPERSTATE: // operational state
if (*payloadPtr != IF_OPER_UNKNOWN) {
// override the flag
- iface->flags &= ~QNetworkInterface::IsUp;
+ iface->flags &= ~QNetworkInterface::IsRunning;
if (*payloadPtr == IF_OPER_UP)
- iface->flags |= QNetworkInterface::IsUp;
+ iface->flags |= QNetworkInterface::IsRunning;
}
break;
}
@@ -345,7 +312,7 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
// find the interface this is relevant to
QNetworkInterfacePrivate *iface = nullptr;
- for (auto candidate : qAsConst(result)) {
+ for (auto candidate : std::as_const(result)) {
if (candidate->index != int(ifa->ifa_index))
continue;
iface = candidate;
@@ -419,6 +386,9 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
}
}
+ if (ifa->ifa_family == AF_INET6 && (ifa->ifa_flags & IFA_F_DADFAILED))
+ return;
+
// now handle flags
QNetworkInterfacePrivate::calculateDnsEligibility(&entry,
flags & IFA_F_TEMPORARY,
diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h
index b879a397f2..2a24826a3e 100644
--- a/src/network/kernel/qnetworkinterface_p.h
+++ b/src/network/kernel/qnetworkinterface_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKINTERFACEPRIVATE_H
#define QNETWORKINTERFACEPRIVATE_H
@@ -56,7 +20,6 @@
#include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qlist.h>
-#include <QtCore/qreadwritelock.h>
#include <QtCore/qstring.h>
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qabstractsocket.h>
@@ -104,14 +67,15 @@ public:
{
// this implements an algorithm that yields the same results as Windows
// produces, for the same input (as far as I can test)
- if (isTemporary || isDeprecated)
+ if (isTemporary || isDeprecated) {
entry->setDnsEligibility(QNetworkAddressEntry::DnsIneligible);
-
- AddressClassification cl = QHostAddressPrivate::classify(entry->ip());
- if (cl == LoopbackAddress || cl == LinkLocalAddress)
- entry->setDnsEligibility(QNetworkAddressEntry::DnsIneligible);
- else
- entry->setDnsEligibility(QNetworkAddressEntry::DnsEligible);
+ } else {
+ AddressClassification cl = QHostAddressPrivate::classify(entry->ip());
+ if (cl == LoopbackAddress || cl == LinkLocalAddress)
+ entry->setDnsEligibility(QNetworkAddressEntry::DnsIneligible);
+ else
+ entry->setDnsEligibility(QNetworkAddressEntry::DnsEligible);
+ }
}
private:
diff --git a/src/network/kernel/qnetworkinterface_uikit_p.h b/src/network/kernel/qnetworkinterface_uikit_p.h
index ea40e74f5c..49e2db007b 100644
--- a/src/network/kernel/qnetworkinterface_uikit_p.h
+++ b/src/network/kernel/qnetworkinterface_uikit_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKINTERFACE_UIKIT_P_H
#define QNETWORKINTERFACE_UIKIT_P_H
@@ -237,26 +201,5 @@ struct in6_ifreq {
#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq)
-// The definition below is ONLY a temporary workaround to unblock
-// integrations on CI. MUST be removed ASAP, as soon as SDK is
-// updated. Currently, we have WatchOS SDK 3.2 and it's missing
-// net/if_types.h (unlike SDK 4.0, which has it). Alas, we have to
-// work this around. We only define constants that we use in code.
-
-#if !QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, __IPHONE_NA, __TVOS_NA, __WATCHOS_4_0)
-
-#define QT_WATCHOS_OUTDATED_SDK_WORKAROUND
-
-#define IFT_PPP 0x17 /* RFC 1331 */
-#define IFT_LOOP 0x18 /* loopback */
-#define IFT_SLIP 0x1c /* IP over generic TTY */
-
-#define IFT_GIF 0x37 /*0xf0*/
-#define IFT_STF 0x39 /*0xf3*/
-
-#define IFT_IEEE1394 0x90 /* IEEE1394 High Performance SerialBus*/
-
-#endif // WatchOS SDK below 4.0
-
#endif // QNETWORKINTERFACE_UIKIT_P_H
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 4c57bff3bc..39ff8dbb92 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qbytearray.h"
#include "qset.h"
@@ -45,17 +9,15 @@
#include "qnetworkinterface_unix_p.h"
#include "qalgorithms.h"
+#include <QtCore/private/qduplicatetracker_p.h>
+
#ifndef QT_NO_NETWORKINTERFACE
#if defined(QT_NO_CLOCK_MONOTONIC)
# include "qdatetime.h"
#endif
-#if defined(QT_LINUXBASE)
-# define QT_NO_GETIFADDRS
-#endif
-
-#ifndef QT_NO_GETIFADDRS
+#if QT_CONFIG(getifaddrs)
# include <ifaddrs.h>
#endif
@@ -90,36 +52,49 @@ static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QSt
}
}
return address;
+}
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_index) != 0, int> = 0)
+{
+ return req.ifr_index;
+}
+
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_ifindex) != 0, int> = 0)
+{
+ return req.ifr_ifindex;
}
uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
{
-#ifndef QT_NO_IPV6IFNAME
- return ::if_nametoindex(name.toLatin1());
+#if QT_CONFIG(ipv6ifname)
+ return ::if_nametoindex(name.toLatin1().constData());
#elif defined(SIOCGIFINDEX)
struct ifreq req;
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket < 0)
return 0;
- QByteArray name8bit = name.toLatin1();
+ const QByteArray name8bit = name.toLatin1();
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, name8bit, qMin<int>(name8bit.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!name8bit.isNull())
+ memcpy(req.ifr_name, name8bit.data(), qMin(size_t(name8bit.length()) + 1, sizeof(req.ifr_name) - 1));
uint id = 0;
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
- id = req.ifr_ifindex;
+ id = ifreq_index(req);
qt_safe_close(socket);
return id;
#else
+ Q_UNUSED(name);
return 0;
#endif
}
QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
{
-#ifndef QT_NO_IPV6IFNAME
+#if QT_CONFIG(ipv6ifname)
char buf[IF_NAMESIZE];
if (::if_indextoname(index, buf))
return QString::fromLatin1(buf);
@@ -128,8 +103,7 @@ QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket >= 0) {
memset(&req, 0, sizeof(ifreq));
- req.ifr_ifindex = index;
-
+ ifreq_index(req) = index;
if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
qt_safe_close(socket);
return QString::fromLatin1(req.ifr_name);
@@ -149,13 +123,13 @@ static int getMtu(int socket, struct ifreq *req)
return 0;
}
-#ifdef QT_NO_GETIFADDRS
+#if !QT_CONFIG(getifaddrs)
// getifaddrs not available
static QSet<QByteArray> interfaceNames(int socket)
{
QSet<QByteArray> result;
-#ifdef QT_NO_IPV6IFNAME
+#if !QT_CONFIG(ipv6ifname)
QByteArray storageBuffer;
struct ifconf interfaceList;
static const int STORAGEBUFFER_GROWTH = 256;
@@ -207,18 +181,14 @@ static QSet<QByteArray> interfaceNames(int socket)
static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfacePrivate *> &interfaces,
struct ifreq &req)
{
- QNetworkInterfacePrivate *iface = 0;
+ QNetworkInterfacePrivate *iface = nullptr;
int ifindex = 0;
-#if !defined(QT_NO_IPV6IFNAME) || defined(SIOCGIFINDEX)
+#if QT_CONFIG(ipv6ifname) || defined(SIOCGIFINDEX)
// Get the interface index
# ifdef SIOCGIFINDEX
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
-# if defined(Q_OS_HAIKU)
- ifindex = req.ifr_index;
-# else
- ifindex = req.ifr_ifindex;
-# endif
+ ifindex = ifreq_index(req);
# else
ifindex = if_nametoindex(req.ifr_name);
# endif
@@ -232,10 +202,11 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa
break;
}
#else
+ Q_UNUSED(socket);
// Search by name
QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
for ( ; if_it != interfaces.end(); ++if_it)
- if ((*if_it)->name == QLatin1String(req.ifr_name)) {
+ if ((*if_it)->name == QLatin1StringView(req.ifr_name)) {
// existing interface
iface = *if_it;
break;
@@ -265,7 +236,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
for ( ; it != names.constEnd(); ++it) {
ifreq req;
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1));
+ if (!it->isNull())
+ memcpy(req.ifr_name, it->constData(), qMin(size_t(it->length()) + 1, sizeof(req.ifr_name) - 1));
QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req);
@@ -276,7 +248,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
iface->name = QString::fromLatin1(req.ifr_name);
// reset the name:
- memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!oldName.isNull())
+ memcpy(req.ifr_name, oldName.constData(), qMin(size_t(oldName.length()) + 1, sizeof(req.ifr_name) - 1));
} else
#endif
{
@@ -337,15 +310,23 @@ QT_BEGIN_INCLUDE_NAMESPACE
QT_END_INCLUDE_NAMESPACE
# endif
+static int openSocket(int &socket)
+{
+ if (socket == -1)
+ socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
+ return socket;
+}
+
# if defined(Q_OS_LINUX) && __GLIBC__ - 0 >= 2 && __GLIBC_MINOR__ - 0 >= 1 && !defined(QT_LINUXBASE)
# include <netpacket/packet.h>
static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
{
- Q_UNUSED(getMtu)
+ Q_UNUSED(getMtu);
+ Q_UNUSED(openSocket);
QList<QNetworkInterfacePrivate *> interfaces;
- QSet<QString> seenInterfaces;
- QVarLengthArray<int, 16> seenIndexes; // faster than QSet<int>
+ QDuplicateTracker<QString> seenInterfaces;
+ QDuplicateTracker<int> seenIndexes;
// On Linux, glibc, uClibc and MUSL obtain the address listing via two
// netlink calls: first an RTM_GETLINK to obtain the interface listing,
@@ -364,9 +345,9 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
iface->flags = convertFlags(ptr->ifa_flags);
iface->hardwareAddress = iface->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr);
- Q_ASSERT(!seenIndexes.contains(iface->index));
- seenIndexes.append(iface->index);
- seenInterfaces.insert(iface->name);
+ const bool sawIfaceIndex = seenIndexes.hasSeen(iface->index);
+ Q_ASSERT(!sawIfaceIndex);
+ (void)seenInterfaces.hasSeen(iface->name);
}
}
@@ -376,16 +357,13 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
if (!ptr->ifa_addr || ptr->ifa_addr->sa_family != AF_PACKET) {
QString name = QString::fromLatin1(ptr->ifa_name);
- if (seenInterfaces.contains(name))
+ if (seenInterfaces.hasSeen(name))
continue;
int ifindex = if_nametoindex(ptr->ifa_name);
- if (seenIndexes.contains(ifindex))
+ if (seenIndexes.hasSeen(ifindex))
continue;
- seenInterfaces.insert(name);
- seenIndexes.append(ifindex);
-
QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
interfaces << iface;
iface->name = name;
@@ -401,7 +379,7 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa
{
Q_UNUSED(entry);
Q_UNUSED(sa);
- Q_UNUSED(ifname)
+ Q_UNUSED(ifname);
}
# elif defined(Q_OS_BSD4)
@@ -409,10 +387,7 @@ QT_BEGIN_INCLUDE_NAMESPACE
# include <net/if_dl.h>
#if defined(QT_PLATFORM_UIKIT)
# include "qnetworkinterface_uikit_p.h"
-#if !defined(QT_WATCHOS_OUTDATED_SDK_WORKAROUND)
-// TODO: remove it as soon as SDK is updated on CI!!!
# include <net/if_types.h>
-#endif
#else
# include <net/if_media.h>
# include <net/if_types.h>
@@ -420,13 +395,6 @@ QT_BEGIN_INCLUDE_NAMESPACE
#endif // QT_PLATFORM_UIKIT
QT_END_INCLUDE_NAMESPACE
-static int openSocket(int &socket)
-{
- if (socket == -1)
- socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
- return socket;
-}
-
static QNetworkInterface::InterfaceType probeIfType(int socket, int iftype, struct ifmediareq *req)
{
// Determine the interface type.
@@ -488,8 +456,9 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
memset(&mediareq, 0, sizeof(mediareq));
// ensure both structs start with the name field, of size IFNAMESIZ
- Q_STATIC_ASSERT(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name));
- Q_ASSERT(&mediareq.ifm_name == &req.ifr_name);
+ static_assert(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name));
+ static_assert(offsetof(struct ifmediareq, ifm_name) == 0);
+ static_assert(offsetof(struct ifreq, ifr_name) == 0);
// on NetBSD we use AF_LINK and sockaddr_dl
// scan the list for that family
@@ -569,8 +538,8 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa
static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
{
- Q_UNUSED(getMtu)
QList<QNetworkInterfacePrivate *> interfaces;
+ int socket = -1;
// make sure there's one entry for each interface
for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
@@ -591,9 +560,18 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
iface->index = ifindex;
iface->name = QString::fromLatin1(ptr->ifa_name);
iface->flags = convertFlags(ptr->ifa_flags);
+
+ if ((socket = openSocket(socket)) >= 0) {
+ struct ifreq ifr;
+ qstrncpy(ifr.ifr_name, ptr->ifa_name, sizeof(ifr.ifr_name));
+ iface->mtu = getMtu(socket, &ifr);
+ }
}
}
+ if (socket != -1)
+ qt_safe_close(socket);
+
return interfaces;
}
@@ -601,7 +579,7 @@ static void getAddressExtraInfo(QNetworkAddressEntry *entry, struct sockaddr *sa
{
Q_UNUSED(entry);
Q_UNUSED(sa);
- Q_UNUSED(ifname)
+ Q_UNUSED(ifname);
}
# endif
@@ -618,8 +596,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
interfaces = createInterfaces(interfaceListing);
for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) {
// Find the interface
- QLatin1String name(ptr->ifa_name);
- QNetworkInterfacePrivate *iface = 0;
+ QLatin1StringView name(ptr->ifa_name);
+ QNetworkInterfacePrivate *iface = nullptr;
QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
for ( ; if_it != interfaces.end(); ++if_it)
if ((*if_it)->name == name) {
diff --git a/src/network/kernel/qnetworkinterface_unix_p.h b/src/network/kernel/qnetworkinterface_unix_p.h
index e5c8909eca..80af9c8e0d 100644
--- a/src/network/kernel/qnetworkinterface_unix_p.h
+++ b/src/network/kernel/qnetworkinterface_unix_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2017 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2017 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKINTERFACE_UNIX_P_H
#define QNETWORKINTERFACE_UNIX_P_H
diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp
index a8d56a9b11..20e75139db 100644
--- a/src/network/kernel/qnetworkinterface_win.cpp
+++ b/src/network/kernel/qnetworkinterface_win.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#define WIN32_LEAN_AND_MEAN 1
diff --git a/src/network/kernel/qnetworkinterface_winrt.cpp b/src/network/kernel/qnetworkinterface_winrt.cpp
deleted file mode 100644
index 24ac3df52f..0000000000
--- a/src/network/kernel/qnetworkinterface_winrt.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkinterface.h"
-#include "qnetworkinterface_p.h"
-
-#ifndef QT_NO_NETWORKINTERFACE
-
-#include <qfunctions_winrt.h>
-
-#include <wrl.h>
-#include <windows.foundation.h>
-#include <windows.foundation.collections.h>
-#include <windows.networking.h>
-#include <windows.networking.connectivity.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Foundation::Collections;
-using namespace ABI::Windows::Networking;
-using namespace ABI::Windows::Networking::Connectivity;
-
-#include <qhostinfo.h>
-
-QT_BEGIN_NAMESPACE
-
-struct HostNameInfo {
- GUID adapterId;
- unsigned char prefixLength;
- QString address;
-};
-
-uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
-{
- // TBD - may not be possible
- Q_UNUSED(name);
- return 0;
-}
-
-QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
-{
- // TBD - may not be possible
- return QString::number(index);
-}
-
-static QNetworkInterfacePrivate *interfaceFromProfile(IConnectionProfile *profile, QList<HostNameInfo> *hostList)
-{
- if (!profile)
- return 0;
-
- QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
-
- NetworkConnectivityLevel connectivityLevel;
- HRESULT hr = profile->GetNetworkConnectivityLevel(&connectivityLevel);
- Q_ASSERT_SUCCEEDED(hr);
- if (connectivityLevel != NetworkConnectivityLevel_None)
- iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
- iface->flags |= QNetworkInterface::CanBroadcast;
-
- ComPtr<INetworkAdapter> adapter;
- hr = profile->get_NetworkAdapter(&adapter);
- // Indicates that no internet connection is available/the device is in airplane mode
- if (hr == E_INVALIDARG)
- return 0;
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 type;
- hr = adapter->get_IanaInterfaceType(&type);
- Q_ASSERT_SUCCEEDED(hr);
- if (type == 23)
- iface->flags |= QNetworkInterface::IsPointToPoint;
- GUID id;
- hr = adapter->get_NetworkAdapterId(&id);
- Q_ASSERT_SUCCEEDED(hr);
- OLECHAR adapterName[39]={0};
- StringFromGUID2(id, adapterName, 39);
- iface->name = QString::fromWCharArray(adapterName);
-
- // According to http://stackoverflow.com/questions/12936193/how-unique-is-the-ethernet-network-adapter-id-in-winrt-it-is-derived-from-the-m
- // obtaining the MAC address using WinRT API is impossible
- // iface->hardwareAddress = ?
-
- for (int i = 0; i < hostList->length(); ++i) {
- const HostNameInfo hostInfo = hostList->at(i);
- if (id != hostInfo.adapterId)
- continue;
-
- QNetworkAddressEntry entry;
- entry.setIp(QHostAddress(hostInfo.address));
- entry.setPrefixLength(hostInfo.prefixLength);
- iface->addressEntries << entry;
-
- hostList->takeAt(i);
- --i;
- }
- return iface;
-}
-
-static QList<QNetworkInterfacePrivate *> interfaceListing()
-{
- QList<QNetworkInterfacePrivate *> interfaces;
-
- QList<HostNameInfo> hostList;
-
- ComPtr<INetworkInformationStatics> hostNameStatics;
- HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &hostNameStatics);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IVectorView<HostName *>> hostNames;
- hr = hostNameStatics->GetHostNames(&hostNames);
- Q_ASSERT_SUCCEEDED(hr);
- if (!hostNames)
- return interfaces;
-
- unsigned int hostNameCount;
- hr = hostNames->get_Size(&hostNameCount);
- Q_ASSERT_SUCCEEDED(hr);
- for (unsigned i = 0; i < hostNameCount; ++i) {
- HostNameInfo hostInfo;
- ComPtr<IHostName> hostName;
- hr = hostNames->GetAt(i, &hostName);
- Q_ASSERT_SUCCEEDED(hr);
-
- HostNameType type;
- hr = hostName->get_Type(&type);
- Q_ASSERT_SUCCEEDED(hr);
- if (type == HostNameType_DomainName)
- continue;
-
- ComPtr<IIPInformation> ipInformation;
- hr = hostName->get_IPInformation(&ipInformation);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<INetworkAdapter> currentAdapter;
- hr = ipInformation->get_NetworkAdapter(&currentAdapter);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = currentAdapter->get_NetworkAdapterId(&hostInfo.adapterId);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IReference<unsigned char>> prefixLengthReference;
- hr = ipInformation->get_PrefixLength(&prefixLengthReference);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = prefixLengthReference->get_Value(&hostInfo.prefixLength);
- Q_ASSERT_SUCCEEDED(hr);
-
- // invalid prefixes
- if ((type == HostNameType_Ipv4 && hostInfo.prefixLength > 32)
- || (type == HostNameType_Ipv6 && hostInfo.prefixLength > 128))
- continue;
-
- HString name;
- hr = hostName->get_CanonicalName(name.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- UINT32 length;
- PCWSTR rawString = name.GetRawBuffer(&length);
- hostInfo.address = QString::fromWCharArray(rawString, length);
-
- hostList << hostInfo;
- }
-
- INetworkInformationStatics *networkInfoStatics;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkInfoStatics);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IConnectionProfile> connectionProfile;
- hr = networkInfoStatics->GetInternetConnectionProfile(&connectionProfile);
- Q_ASSERT_SUCCEEDED(hr);
- QNetworkInterfacePrivate *iface = interfaceFromProfile(connectionProfile.Get(), &hostList);
- if (iface) {
- iface->index = 0;
- interfaces << iface;
- }
-
- ComPtr<IVectorView<ConnectionProfile *>> connectionProfiles;
- hr = networkInfoStatics->GetConnectionProfiles(&connectionProfiles);
- Q_ASSERT_SUCCEEDED(hr);
- if (!connectionProfiles)
- return interfaces;
-
- unsigned int size;
- hr = connectionProfiles->get_Size(&size);
- Q_ASSERT_SUCCEEDED(hr);
- for (unsigned int i = 0; i < size; ++i) {
- ComPtr<IConnectionProfile> profile;
- hr = connectionProfiles->GetAt(i, &profile);
- Q_ASSERT_SUCCEEDED(hr);
-
- iface = interfaceFromProfile(profile.Get(), &hostList);
- if (iface) {
- iface->index = i + 1;
- interfaces << iface;
- }
- }
- return interfaces;
-}
-
-QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
-{
- return interfaceListing();
-}
-
-QString QHostInfo::localDomainName()
-{
- return QString();
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 90c8b75a86..9b91b11d6b 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -189,7 +153,7 @@
QNetworkProxy sets different capabilities by default when the
object is created (see QNetworkProxy::ProxyType for a list of the
- defaults). However, it is possible to change the capabitilies
+ defaults). However, it is possible to change the capabilities
after the object has been created with setCapabilities().
The capabilities that QNetworkProxy supports are:
@@ -241,12 +205,12 @@
#include "qstringlist.h"
#include "qurl.h"
-#ifndef QT_NO_BEARERMANAGEMENT
-#include <QtNetwork/QNetworkConfiguration>
-#endif
-
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN(QNetworkProxy)
+
class QSocks5SocketEngineHandler;
class QHttpSocketEngineHandler;
@@ -358,10 +322,8 @@ QList<QNetworkProxy> QGlobalNetworkProxy::proxyForQuery(const QNetworkProxyQuery
// don't look for proxies for a local connection
QHostAddress parsed;
QString hostname = query.url().host();
- if (hostname == QLatin1String("localhost")
- || hostname.startsWith(QLatin1String("localhost."))
- || (parsed.setAddress(hostname)
- && (parsed.isLoopback()))) {
+ if (hostname == "localhost"_L1 || hostname.startsWith("localhost."_L1)
+ || (parsed.setAddress(hostname) && (parsed.isLoopback()))) {
result << QNetworkProxy(QNetworkProxy::NoProxy);
return result;
}
@@ -790,6 +752,53 @@ QNetworkProxy QNetworkProxy::applicationProxy()
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this network request.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy,
+ default constructed QHttpHeaders is returned.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkProxy::headers() const
+{
+ if (d->type != HttpProxy && d->type != HttpCachingProxy)
+ return {};
+ return d->headers.headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network request, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, the values will
+ be parsed and the corresponding parsed form will also be set.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy this has no
+ effect.
+
+ \sa headers(), QNetworkRequest::KnownHeaders
+*/
+void QNetworkProxy::setHeaders(QHttpHeaders &&newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkProxy::setHeaders(const QHttpHeaders &newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(newHeaders);
+}
+
+/*!
\since 5.0
Returns the value of the known network header \a header if it is
in use for this proxy. If it is not present, returns QVariant()
@@ -833,7 +842,7 @@ bool QNetworkProxy::hasRawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return false;
- return d->headers.findRawHeader(headerName) != d->headers.rawHeaders.constEnd();
+ return d->headers.headers().contains(headerName);
}
/*!
@@ -852,11 +861,7 @@ QByteArray QNetworkProxy::rawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return QByteArray();
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->headers.findRawHeader(headerName);
- if (it != d->headers.rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->headers.rawHeader(headerName);
}
/*!
@@ -984,11 +989,6 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
like choosing an caching HTTP proxy for HTTP-based connections,
but a more powerful SOCKSv5 proxy for all others.
- The network configuration specifies which configuration to use,
- when bearer management is used. For example on a mobile phone
- the proxy settings are likely to be different for the cellular
- network vs WLAN.
-
Some of the criteria may not make sense in all of the types of
query. The following table lists the criteria that are most
commonly used, according to the type of query.
@@ -1133,73 +1133,6 @@ QNetworkProxyQuery::QNetworkProxyQuery(quint16 bindPort, const QString &protocol
d->type = queryType;
}
-#if !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
-/*!
- \deprecated
-
- Constructs a QNetworkProxyQuery with the URL \a requestUrl and
- sets the query type to \a queryType. The specified \a networkConfiguration
- parameter is ignored.
-
- \sa protocolTag(), peerHostName(), peerPort(), networkConfiguration()
-*/
-QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- const QUrl &requestUrl, QueryType queryType)
-{
- Q_UNUSED(networkConfiguration)
- d->remote = requestUrl;
- d->type = queryType;
-}
-
-/*!
- \deprecated
-
- Constructs a QNetworkProxyQuery of type \a queryType and sets the
- protocol tag to be \a protocolTag. This constructor is suitable
- for QNetworkProxyQuery::TcpSocket queries, because it sets the
- peer hostname to \a hostname and the peer's port number to \a
- port. The specified \a networkConfiguration parameter is ignored.
-
- \sa networkConfiguration()
-*/
-QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- const QString &hostname, int port,
- const QString &protocolTag,
- QueryType queryType)
-{
- Q_UNUSED(networkConfiguration);
- d->remote.setScheme(protocolTag);
- d->remote.setHost(hostname);
- d->remote.setPort(port);
- d->type = queryType;
-}
-
-/*!
- \deprecated
-
- Constructs a QNetworkProxyQuery of type \a queryType and sets the
- protocol tag to be \a protocolTag. This constructor is suitable
- for QNetworkProxyQuery::TcpSocket queries because it sets the
- local port number to \a bindPort. The specified \a networkConfiguration
- parameter is ignored.
-
- Note that \a bindPort is of type quint16 to indicate the exact
- port number that is requested. The value of -1 (unknown) is not
- allowed in this context.
-
- \sa localPort(), networkConfiguration()
-*/
-QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- quint16 bindPort, const QString &protocolTag,
- QueryType queryType)
-{
- Q_UNUSED(networkConfiguration);
- d->remote.setScheme(protocolTag);
- d->localPort = bindPort;
- d->type = queryType;
-}
-#endif // !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
-
/*!
Constructs a QNetworkProxyQuery object that is a copy of \a other.
*/
@@ -1422,33 +1355,6 @@ void QNetworkProxyQuery::setUrl(const QUrl &url)
d->remote = url;
}
-#if !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
-/*!
- \deprecated
-
- Returns QNetworkConfiguration().
-
- \sa setNetworkConfiguration()
-*/
-QNetworkConfiguration QNetworkProxyQuery::networkConfiguration() const
-{
- return QNetworkConfiguration();
-}
-
-/*!
- \deprecated
-
- This function does nothing. The specified \a networkConfiguration parameter
- is ignored.
-
- \sa networkConfiguration()
-*/
-void QNetworkProxyQuery::setNetworkConfiguration(const QNetworkConfiguration &networkConfiguration)
-{
- Q_UNUSED(networkConfiguration);
-}
-#endif // !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
-
/*!
\class QNetworkProxyFactory
\brief The QNetworkProxyFactory class provides fine-grained proxy selection.
@@ -1603,13 +1509,18 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
Internet Explorer's settings and use them.
On \macos, this function will obtain the proxy settings using the
- SystemConfiguration framework from Apple. It will apply the FTP,
+ CFNetwork framework from Apple. It will apply the FTP,
HTTP and HTTPS proxy configurations for queries that contain the
protocol tag "ftp", "http" and "https", respectively. If the SOCKS
proxy is enabled in that configuration, this function will use the
SOCKS server for all queries. If SOCKS isn't enabled, it will use
the HTTPS proxy for all TcpSocket and UrlRequest queries.
+ On systems configured with libproxy support, this function will
+ rely on libproxy to obtain the proxy settings. Depending on
+ libproxy configurations, this can in turn delegate to desktop
+ settings, environment variables, etc.
+
On other systems, this function will pick up proxy settings from
the "http_proxy" environment variable. This variable must be a URL
using one of the following schemes: "http", "socks5" or "socks5h".
@@ -1621,9 +1532,6 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
listed here.
\list
- \li On \macos, this function will ignore the Proxy Auto Configuration
- settings, since it cannot execute the associated ECMAScript code.
-
\li On Windows platforms, this function may take several seconds to
execute depending on the configuration of the user's system.
\endlist
@@ -1688,7 +1596,7 @@ QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
scaps << QStringLiteral("SctpTunnel");
if (caps & QNetworkProxy::SctpListeningCapability)
scaps << QStringLiteral("SctpListen");
- debug << '[' << scaps.join(QLatin1Char(' ')) << ']';
+ debug << '[' << scaps.join(u' ') << ']';
return debug;
}
@@ -1710,4 +1618,6 @@ QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery)
QT_END_NAMESPACE
+#include "moc_qnetworkproxy.cpp"
+
#endif // QT_NO_NETWORKPROXY
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index 302a2ce6ca..9f92ffeb12 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKPROXY_H
#define QNETWORKPROXY_H
@@ -51,7 +15,6 @@ QT_BEGIN_NAMESPACE
class QUrl;
-class QNetworkConfiguration;
class QNetworkProxyQueryPrivate;
class Q_NETWORK_EXPORT QNetworkProxyQuery
@@ -75,25 +38,12 @@ public:
QueryType queryType = TcpSocket);
explicit QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag = QString(),
QueryType queryType = TcpServer);
-#if !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
- Q_DECL_DEPRECATED_X("QNetworkConfiguration support in QNetworkProxy is deprecated")
- QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- const QUrl &requestUrl, QueryType queryType = UrlRequest);
- Q_DECL_DEPRECATED_X("QNetworkConfiguration support in QNetworkProxy is deprecated")
- QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- const QString &hostname, int port, const QString &protocolTag = QString(),
- QueryType queryType = TcpSocket);
- Q_DECL_DEPRECATED_X("QNetworkConfiguration support in QNetworkProxy is deprecated")
- QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
- quint16 bindPort, const QString &protocolTag = QString(),
- QueryType queryType = TcpServer);
-#endif
QNetworkProxyQuery(const QNetworkProxyQuery &other);
QNetworkProxyQuery &operator=(QNetworkProxyQuery &&other) noexcept { swap(other); return *this; }
QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other);
~QNetworkProxyQuery();
- void swap(QNetworkProxyQuery &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkProxyQuery &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkProxyQuery &other) const;
inline bool operator!=(const QNetworkProxyQuery &other) const
@@ -117,13 +67,6 @@ public:
QUrl url() const;
void setUrl(const QUrl &url);
-#if !defined(QT_NO_BEARERMANAGEMENT) && QT_DEPRECATED_SINCE(5, 10)
- Q_DECL_DEPRECATED_X("QNetworkConfiguration support in QNetworkProxy is deprecated")
- QNetworkConfiguration networkConfiguration() const;
- Q_DECL_DEPRECATED_X("QNetworkConfiguration support in QNetworkProxy is deprecated")
- void setNetworkConfiguration(const QNetworkConfiguration &networkConfiguration);
-#endif
-
private:
QSharedDataPointer<QNetworkProxyQueryPrivate> d;
};
@@ -134,6 +77,7 @@ class QNetworkProxyPrivate;
class Q_NETWORK_EXPORT QNetworkProxy
{
+ Q_GADGET
public:
enum ProxyType {
DefaultProxy,
@@ -163,7 +107,7 @@ public:
QNetworkProxy &operator=(const QNetworkProxy &other);
~QNetworkProxy();
- void swap(QNetworkProxy &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkProxy &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkProxy &other) const;
inline bool operator!=(const QNetworkProxy &other) const
@@ -192,6 +136,10 @@ public:
static void setApplicationProxy(const QNetworkProxy &proxy);
static QNetworkProxy applicationProxy();
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+
// "cooked" headers
QVariant header(QNetworkRequest::KnownHeaders header) const;
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
@@ -231,7 +179,7 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxy
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkProxy)
+QT_DECL_METATYPE_EXTERN(QNetworkProxy, Q_NETWORK_EXPORT)
#endif // QT_NO_NETWORKPROXY
diff --git a/src/network/kernel/qnetworkproxy_android.cpp b/src/network/kernel/qnetworkproxy_android.cpp
new file mode 100644
index 0000000000..3d37266b70
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_android.cpp
@@ -0,0 +1,85 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qnetworkproxy.h"
+
+#include <QtCore/qcoreapplication_platform.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+
+#ifndef QT_NO_NETWORKPROXY
+
+QT_BEGIN_NAMESPACE
+
+struct ProxyInfoObject
+{
+public:
+ ProxyInfoObject();
+ ~ProxyInfoObject();
+};
+
+using namespace QNativeInterface;
+
+Q_GLOBAL_STATIC(ProxyInfoObject, proxyInfoInstance)
+
+static const char networkClass[] = "org/qtproject/qt/android/network/QtNetwork";
+
+Q_DECLARE_JNI_CLASS(ProxyInfo, "android/net/ProxyInfo")
+Q_DECLARE_JNI_TYPE(JStringArray, "[Ljava/lang/String;")
+
+ProxyInfoObject::ProxyInfoObject()
+{
+ QJniObject::callStaticMethod<void>(networkClass,
+ "registerReceiver",
+ QAndroidApplication::context());
+}
+
+ProxyInfoObject::~ProxyInfoObject()
+{
+ QJniObject::callStaticMethod<void>(networkClass,
+ "unregisterReceiver",
+ QAndroidApplication::context());
+}
+
+QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> proxyList;
+ if (!proxyInfoInstance)
+ return proxyList;
+
+ QJniObject proxyInfo = QJniObject::callStaticObjectMethod<QtJniTypes::ProxyInfo>(
+ networkClass, "getProxyInfo", QAndroidApplication::context());
+ if (proxyInfo.isValid()) {
+ QJniObject exclusionList =
+ proxyInfo.callObjectMethod<QtJniTypes::JStringArray>("getExclusionList");
+ bool exclude = false;
+ if (exclusionList.isValid()) {
+ jobjectArray listObject = exclusionList.object<jobjectArray>();
+ QJniEnvironment env;
+ QJniObject entry;
+ const int size = env->GetArrayLength(listObject);
+ QUrl host = QUrl(query.url().host());
+ for (int i = 0; i < size; ++i) {
+ entry = env->GetObjectArrayElement(listObject, i);
+ if (host.matches(QUrl(entry.toString()), QUrl::RemoveScheme)) {
+ exclude = true;
+ break;
+ }
+ }
+ }
+ if (!exclude) {
+ QJniObject hostName = proxyInfo.callObjectMethod<jstring>("getHost");
+ const int port = proxyInfo.callMethod<jint>("getPort");
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy, hostName.toString(), port);
+ proxyList << proxy;
+ }
+ }
+ if (proxyList.isEmpty())
+ proxyList << QNetworkProxy::NoProxy;
+
+ return proxyList;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_darwin.cpp
index 67fda24ea6..d2bd4958dd 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_darwin.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkproxy.h"
@@ -45,11 +9,12 @@
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QStringList>
#include <QtCore/QUrl>
#include <QtCore/qendian.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qsystemdetection.h>
#include "private/qcore_mac_p.h"
/*
@@ -68,25 +33,33 @@
* \li Bypass list (by default: *.local, 169.254/16)
* \endlist
*
- * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies
- * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See
+ * The matching configuration can be obtained by calling CFNetworkCopySystemProxySettings()
+ * (from <CFNetwork/CFProxySupport.h>). See
* Apple's documentation:
*
- * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies
+ * https://developer.apple.com/documentation/cfnetwork/1426754-cfnetworkcopysystemproxysettings?language=objc
*
*/
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
{
+ Q_ASSERT(dict);
+
if (host.isEmpty())
return true;
- bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
+#ifndef Q_OS_IOS
+ // On iOS all those keys are not available, and worse so - entries
+ // for HTTPS are not in the dictionary, but instead in some nested dictionary
+ // with undocumented keys/object types.
+ bool isSimple = !host.contains(u'.') && !host.contains(u':');
CFNumberRef excludeSimples;
if (isSimple &&
- (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) {
+ (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExcludeSimpleHostnames))) {
int enabled;
if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled)
return true;
@@ -97,7 +70,7 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
// not a simple host name
// does it match the list of exclusions?
- CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList);
+ CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExceptionsList);
if (!exclusionList)
return false;
@@ -110,12 +83,14 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
return true; // excluded
} else {
// do wildcard matching
- QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
- if (rx.exactMatch(host))
+ auto rx = QRegularExpression::fromWildcard(entry, Qt::CaseInsensitive);
+ if (rx.match(host).hasMatch())
return true;
}
}
-
+#else
+ Q_UNUSED(dict);
+#endif // Q_OS_IOS
// host was not excluded
return false;
}
@@ -146,7 +121,6 @@ static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::Pr
return QNetworkProxy();
}
-
static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict)
{
QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
@@ -203,16 +177,52 @@ void proxyAutoConfigCallback(void *client, CFArrayRef proxylist, CFErrorRef erro
info->proxies = proxylist;
}
}
-} // anon namespace
+
+QCFType<CFStringRef> stringByAddingPercentEscapes(CFStringRef originalPath)
+{
+ Q_ASSERT(originalPath);
+ const auto qtPath = QString::fromCFString(originalPath);
+ const auto escaped = QString::fromUtf8(QUrl(qtPath).toEncoded());
+ return escaped.toCFString();
+}
+
+#ifdef Q_OS_IOS
+QList<QNetworkProxy> proxiesForQueryUrl(CFDictionaryRef dict, const QUrl &url)
+{
+ Q_ASSERT(dict);
+
+ const QCFType<CFURLRef> cfUrl = url.toCFURL();
+ const QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForURL(cfUrl, dict);
+ Q_ASSERT(proxies);
+
+ QList<QNetworkProxy> result;
+ const auto count = CFArrayGetCount(proxies);
+ if (!count) // Could be no proper proxy or host excluded.
+ return result;
+
+ for (CFIndex i = 0; i < count; ++i) {
+ const void *obj = CFArrayGetValueAtIndex(proxies, i);
+ if (CFGetTypeID(obj) != CFDictionaryGetTypeID())
+ continue;
+ const QNetworkProxy proxy = proxyFromDictionary(static_cast<CFDictionaryRef>(obj));
+ if (proxy.type() == QNetworkProxy::NoProxy || proxy.type() == QNetworkProxy::DefaultProxy)
+ continue;
+ result << proxy;
+ }
+
+ return result;
+}
+#endif // Q_OS_IOS
+} // unnamed namespace.
QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
{
QList<QNetworkProxy> result;
// obtain a dictionary to the proxy settings:
- const QCFType<CFDictionaryRef> dict = SCDynamicStoreCopyProxies(NULL);
+ const QCFType<CFDictionaryRef> dict = CFNetworkCopySystemProxySettings();
if (!dict) {
- qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
+ qWarning("QNetworkProxyFactory::systemProxyForQuery: CFNetworkCopySystemProxySettings returned nullptr");
return result; // failed
}
@@ -221,16 +231,14 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
// is there a PAC enabled? If so, use it first.
CFNumberRef pacEnabled;
- if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) {
+ if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigEnable))) {
int enabled;
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
// PAC is enabled
// kSCPropNetProxiesProxyAutoConfigURLString returns the URL string
// as entered in the system proxy configuration dialog
- CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
- QCFType<CFStringRef> cfPacLocation = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, pacLocationSetting, NULL, NULL,
- kCFStringEncodingUTF8);
-
+ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigURLString);
+ auto cfPacLocation = stringByAddingPercentEscapes(pacLocationSetting);
QCFType<CFDataRef> pacData;
QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
if (!pacUrl) {
@@ -279,53 +287,77 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
}
}
- // no PAC, decide which proxy we're looking for based on the query
- bool isHttps = false;
- QString protocol = query.protocolTag().toLower();
-
+ // No PAC, decide which proxy we're looking for based on the query
// try the protocol-specific proxy
+ const QString protocol = query.protocolTag();
QNetworkProxy protocolSpecificProxy;
- if (protocol == QLatin1String("ftp")) {
- protocolSpecificProxy =
- proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
- kSCPropNetProxiesFTPEnable,
- kSCPropNetProxiesFTPProxy,
- kSCPropNetProxiesFTPPort);
- } else if (protocol == QLatin1String("http")) {
+ if (protocol.compare("http"_L1, Qt::CaseInsensitive) == 0) {
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPEnable,
- kSCPropNetProxiesHTTPProxy,
- kSCPropNetProxiesHTTPPort);
- } else if (protocol == QLatin1String("https")) {
+ kCFNetworkProxiesHTTPEnable,
+ kCFNetworkProxiesHTTPProxy,
+ kCFNetworkProxiesHTTPPort);
+ }
+
+
+#ifdef Q_OS_IOS
+ if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy
+ && protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) {
+ // HTTP proxy is enabled (on iOS there is no separate HTTPS, though
+ // 'dict' contains deeply buried entries which are the same as HTTP.
+ result << protocolSpecificProxy;
+ }
+
+ // TODO: check query.queryType()? It's possible, the exclude list
+ // did exclude it but above we added a proxy because HTTP proxy
+ // is found. We'll deal with such a situation later, since now NMI.
+ const auto proxiesForUrl = proxiesForQueryUrl(dict, query.url());
+ for (const auto &proxy : proxiesForUrl) {
+ if (!result.contains(proxy))
+ result << proxy;
+ }
+#else
+ bool isHttps = false;
+ if (protocol.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
+ kCFNetworkProxiesFTPEnable,
+ kCFNetworkProxiesFTPProxy,
+ kCFNetworkProxiesFTPPort);
+ } else if (protocol.compare("https"_L1, Qt::CaseInsensitive) == 0) {
isHttps = true;
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
}
+
if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy)
result << protocolSpecificProxy;
// let's add SOCKSv5 if present too
QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy,
- kSCPropNetProxiesSOCKSEnable,
- kSCPropNetProxiesSOCKSProxy,
- kSCPropNetProxiesSOCKSPort);
+ kCFNetworkProxiesSOCKSEnable,
+ kCFNetworkProxiesSOCKSProxy,
+ kCFNetworkProxiesSOCKSPort);
if (socks5.type() != QNetworkProxy::DefaultProxy)
result << socks5;
// let's add the HTTPS proxy if present (and if we haven't added
// yet)
if (!isHttps) {
- QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ QNetworkProxy https;
+ https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
+
+
if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy)
result << https;
}
+#endif // !Q_OS_IOS
return result;
}
diff --git a/src/network/kernel/qnetworkproxy_generic.cpp b/src/network/kernel/qnetworkproxy_generic.cpp
index 3ff0cc5168..b915ee8fc8 100644
--- a/src/network/kernel/qnetworkproxy_generic.cpp
+++ b/src/network/kernel/qnetworkproxy_generic.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkproxy.h"
@@ -51,36 +15,42 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static bool ignoreProxyFor(const QNetworkProxyQuery &query)
{
const QByteArray noProxy = qgetenv("no_proxy").trimmed();
if (noProxy.isEmpty())
return false;
+ const QString host = query.peerHostName();
+
const QList<QByteArray> noProxyTokens = noProxy.split(',');
for (const QByteArray &rawToken : noProxyTokens) {
- QByteArray token = rawToken.trimmed();
- QString peerHostName = query.peerHostName();
+ auto token = QLatin1StringView(rawToken).trimmed();
// Since we use suffix matching, "*" is our 'default' behaviour
- if (token.startsWith('*'))
+ if (token.startsWith(u'*'))
token = token.mid(1);
// Harmonize trailing dot notation
- if (token.endsWith('.') && !peerHostName.endsWith('.'))
- token = token.left(token.length()-1);
+ if (token.endsWith(u'.') && !host.endsWith(u'.'))
+ token = token.chopped(1);
- // We prepend a dot to both values, so that when we do a suffix match,
- // we don't match "donotmatch.com" with "match.com"
- if (!token.startsWith('.'))
- token.prepend('.');
+ if (token.startsWith(u'.')) // leading dot is implied
+ token = token.mid(1);
- if (!peerHostName.startsWith('.'))
- peerHostName.prepend('.');
+ if (host.endsWith(token)) {
- if (peerHostName.endsWith(QLatin1String(token)))
- return true;
+ // Make sure that when we have a suffix match,
+ // we don't match "donotmatch.com" with "match.com"
+
+ if (host.size() == token.size()) // iow: host == token
+ return true;
+ if (host[host.size() - token.size() - 1] == u'.') // match follows a dot
+ return true;
+ }
}
return false;
@@ -97,11 +67,11 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
const QString queryProtocol = query.protocolTag();
QByteArray proxy_env;
- if (queryProtocol == QLatin1String("http"))
+ if (queryProtocol == "http"_L1)
proxy_env = qgetenv("http_proxy");
- else if (queryProtocol == QLatin1String("https"))
+ else if (queryProtocol == "https"_L1)
proxy_env = qgetenv("https_proxy");
- else if (queryProtocol == QLatin1String("ftp"))
+ else if (queryProtocol == "ftp"_L1)
proxy_env = qgetenv("ftp_proxy");
else
proxy_env = qgetenv("all_proxy");
@@ -113,16 +83,16 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
if (!proxy_env.isEmpty()) {
QUrl url = QUrl(QString::fromLocal8Bit(proxy_env));
const QString scheme = url.scheme();
- if (scheme == QLatin1String("socks5")) {
+ if (scheme == "socks5"_L1) {
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(),
url.port() ? url.port() : 1080, url.userName(), url.password());
proxyList << proxy;
- } else if (scheme == QLatin1String("socks5h")) {
+ } else if (scheme == "socks5h"_L1) {
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(),
url.port() ? url.port() : 1080, url.userName(), url.password());
proxy.setCapabilities(QNetworkProxy::HostNameLookupCapability);
proxyList << proxy;
- } else if ((scheme.isEmpty() || scheme == QLatin1String("http"))
+ } else if ((scheme.isEmpty() || scheme == "http"_L1)
&& query.queryType() != QNetworkProxyQuery::UdpSocket
&& query.queryType() != QNetworkProxyQuery::TcpServer) {
QNetworkProxy proxy(QNetworkProxy::HttpProxy, url.host(),
diff --git a/src/network/kernel/qnetworkproxy_libproxy.cpp b/src/network/kernel/qnetworkproxy_libproxy.cpp
index 29d2a0bd3b..da1e8fdbd4 100644
--- a/src/network/kernel/qnetworkproxy_libproxy.cpp
+++ b/src/network/kernel/qnetworkproxy_libproxy.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2017 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2017 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkproxy.h"
@@ -48,12 +12,15 @@
#include <QtCore/QUrl>
#include <QtCore/private/qeventdispatcher_unix_p.h>
#include <QtCore/private/qthread_p.h>
+#include <QtCore/qapplicationstatic.h>
#include <proxy.h>
#include <dlfcn.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static bool isThreadingNeeded()
{
// Try to guess if the libproxy we linked to is from the libproxy project
@@ -106,7 +73,7 @@ private:
Data *request;
};
-Q_GLOBAL_STATIC(QLibProxyWrapper, libProxyWrapper);
+Q_APPLICATION_STATIC(QLibProxyWrapper, libProxyWrapper)
QLibProxyWrapper::QLibProxyWrapper()
{
@@ -199,13 +166,15 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
break;
// fake URLs to get libproxy to tell us the SOCKS proxy
case QNetworkProxyQuery::TcpSocket:
- queryUrl.setScheme(QStringLiteral("tcp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("tcp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::TunnelingCapability;
break;
case QNetworkProxyQuery::UdpSocket:
- queryUrl.setScheme(QStringLiteral("udp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("udp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::UdpTunnelingCapability;
@@ -221,14 +190,13 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
for (const QUrl& url : rawProxies) {
QNetworkProxy::ProxyType type;
const QString scheme = url.scheme();
- if (scheme == QLatin1String("http")) {
+ if (scheme == "http"_L1) {
type = QNetworkProxy::HttpProxy;
- } else if (scheme == QLatin1String("socks")
- || scheme == QLatin1String("socks5")) {
+ } else if (scheme == "socks"_L1 || scheme == "socks5"_L1) {
type = QNetworkProxy::Socks5Proxy;
- } else if (scheme == QLatin1String("ftp")) {
+ } else if (scheme == "ftp"_L1) {
type = QNetworkProxy::FtpCachingProxy;
- } else if (scheme == QLatin1String("direct")) {
+ } else if (scheme == "direct"_L1) {
type = QNetworkProxy::NoProxy;
haveDirectConnection = true;
} else {
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index 56397814b0..a2daa62e84 100644
--- a/src/network/kernel/qnetworkproxy_win.cpp
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkproxy.h"
@@ -43,111 +7,40 @@
#include <qmutex.h>
#include <qstringlist.h>
-#include <qregexp.h>
+#include <qregularexpression.h>
#include <qurl.h>
-#include <private/qsystemlibrary_p.h>
#include <qnetworkinterface.h>
#include <qdebug.h>
+#include <qvarlengtharray.h>
+#include <qhash.h>
#include <string.h>
#include <qt_windows.h>
-#include <wininet.h>
#include <lmcons.h>
-
-/*
- * Information on the WinHTTP DLL:
- * http://msdn.microsoft.com/en-us/library/aa384122(VS.85).aspx example for WPAD
- *
- * http://msdn.microsoft.com/en-us/library/aa384097(VS.85).aspx WinHttpGetProxyForUrl
- * http://msdn.microsoft.com/en-us/library/aa384096(VS.85).aspx WinHttpGetIEProxyConfigForCurrentUs
- * http://msdn.microsoft.com/en-us/library/aa384095(VS.85).aspx WinHttpGetDefaultProxyConfiguration
- */
-
-// We don't want to include winhttp.h because that's not
-// present in some Windows SDKs (I don't know why)
-// So, instead, copy the definitions here
-
-typedef struct {
- DWORD dwFlags;
- DWORD dwAutoDetectFlags;
- LPCWSTR lpszAutoConfigUrl;
- LPVOID lpvReserved;
- DWORD dwReserved;
- BOOL fAutoLogonIfChallenged;
-} WINHTTP_AUTOPROXY_OPTIONS;
-
-typedef struct {
- DWORD dwAccessType;
- LPWSTR lpszProxy;
- LPWSTR lpszProxyBypass;
-} WINHTTP_PROXY_INFO;
-
-typedef struct {
- BOOL fAutoDetect;
- LPWSTR lpszAutoConfigUrl;
- LPWSTR lpszProxy;
- LPWSTR lpszProxyBypass;
-} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
-
-#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001
-#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002
-
-#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001
-#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002
-
-#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
-#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
-#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
-
-#define WINHTTP_NO_PROXY_NAME NULL
-#define WINHTTP_NO_PROXY_BYPASS NULL
-
-#define WINHTTP_ERROR_BASE 12000
-#define ERROR_WINHTTP_LOGIN_FAILURE (WINHTTP_ERROR_BASE + 15)
-#define ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT (WINHTTP_ERROR_BASE + 167)
-#define ERROR_WINHTTP_AUTODETECTION_FAILED (WINHTTP_ERROR_BASE + 180)
+#include <winhttp.h>
QT_BEGIN_NAMESPACE
-typedef BOOL (WINAPI * PtrWinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS*, WINHTTP_PROXY_INFO*);
-typedef HINTERNET (WINAPI * PtrWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR,DWORD);
-typedef BOOL (WINAPI * PtrWinHttpGetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
-typedef BOOL (WINAPI * PtrWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
-typedef BOOL (WINAPI * PtrWinHttpCloseHandle)(HINTERNET);
-typedef BOOL (WINAPI * PtrCloseServiceHandle)(SC_HANDLE hSCObject);
-static PtrWinHttpGetProxyForUrl ptrWinHttpGetProxyForUrl = 0;
-static PtrWinHttpOpen ptrWinHttpOpen = 0;
-static PtrWinHttpGetDefaultProxyConfiguration ptrWinHttpGetDefaultProxyConfiguration = 0;
-static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCurrentUser = 0;
-static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0;
-
+using namespace Qt::StringLiterals;
static bool currentProcessIsService()
{
- typedef BOOL (WINAPI *PtrGetUserName)(LPTSTR lpBuffer, LPDWORD lpnSize);
- typedef BOOL (WINAPI *PtrLookupAccountName)(LPCTSTR lpSystemName, LPCTSTR lpAccountName, PSID Sid,
- LPDWORD cbSid, LPTSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse);
- static PtrGetUserName ptrGetUserName = (PtrGetUserName)QSystemLibrary::resolve(QLatin1String("Advapi32"), "GetUserNameW");
- static PtrLookupAccountName ptrLookupAccountName = (PtrLookupAccountName)QSystemLibrary::resolve(QLatin1String("Advapi32"), "LookupAccountNameW");
-
- if (ptrGetUserName && ptrLookupAccountName) {
- wchar_t userName[UNLEN + 1] = L"";
- DWORD size = UNLEN;
- if (ptrGetUserName(userName, &size)) {
- SID_NAME_USE type = SidTypeUser;
- DWORD sidSize = 0;
- DWORD domainSize = 0;
- // first call is to get the correct size
- bool bRet = ptrLookupAccountName(NULL, userName, NULL, &sidSize, NULL, &domainSize, &type);
- if (bRet == FALSE && ERROR_INSUFFICIENT_BUFFER != GetLastError())
- return false;
- QVarLengthArray<BYTE, 68> buff(sidSize);
- QVarLengthArray<wchar_t, MAX_PATH> domainName(domainSize);
- // second call to LookupAccountNameW actually gets the SID
- // both the pointer to the buffer and the pointer to the domain name should not be NULL
- if (ptrLookupAccountName(NULL, userName, buff.data(), &sidSize, domainName.data(), &domainSize, &type))
- return type != SidTypeUser; //returns true if the current user is not a user
- }
+ wchar_t userName[UNLEN + 1] = L"";
+ DWORD size = UNLEN;
+ if (GetUserNameW(userName, &size)) {
+ SID_NAME_USE type = SidTypeUser;
+ DWORD sidSize = 0;
+ DWORD domainSize = 0;
+ // first call is to get the correct size
+ bool bRet = LookupAccountNameW(NULL, userName, NULL, &sidSize, NULL, &domainSize, &type);
+ if (bRet == FALSE && ERROR_INSUFFICIENT_BUFFER != GetLastError())
+ return false;
+ QVarLengthArray<BYTE, 68> buff(sidSize);
+ QVarLengthArray<wchar_t, MAX_PATH> domainName(domainSize);
+ // second call to LookupAccountNameW actually gets the SID
+ // both the pointer to the buffer and the pointer to the domain name should not be NULL
+ if (LookupAccountNameW(NULL, userName, buff.data(), &sidSize, domainName.data(), &domainSize, &type))
+ return type != SidTypeUser; //returns true if the current user is not a user
}
return false;
}
@@ -155,11 +48,11 @@ static bool currentProcessIsService()
static QStringList splitSpaceSemicolon(const QString &source)
{
QStringList list;
- int start = 0;
- int end;
+ qsizetype start = 0;
+ qsizetype end;
while (true) {
- int space = source.indexOf(QLatin1Char(' '), start);
- int semicolon = source.indexOf(QLatin1Char(';'), start);
+ qsizetype space = source.indexOf(u' ', start);
+ qsizetype semicolon = source.indexOf(u';', start);
end = space;
if (semicolon != -1 && (end == -1 || semicolon < end))
end = semicolon;
@@ -181,7 +74,7 @@ static bool isBypassed(const QString &host, const QStringList &bypassList)
if (host.isEmpty())
return false;
- bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
+ bool isSimple = !host.contains(u'.') && !host.contains(u':');
QHostAddress ipAddress;
bool isIpAddress = ipAddress.setAddress(host);
@@ -192,7 +85,7 @@ static bool isBypassed(const QString &host, const QStringList &bypassList)
// does it match the list of exclusions?
for (const QString &entry : bypassList) {
- if (entry == QLatin1String("<local>")) {
+ if (entry == "<local>"_L1) {
if (isSimple)
return true;
if (isIpAddress) {
@@ -212,8 +105,8 @@ static bool isBypassed(const QString &host, const QStringList &bypassList)
return true; // excluded
} else {
// do wildcard matching
- QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
- if (rx.exactMatch(host))
+ auto rx = QRegularExpression::fromWildcard(entry, Qt::CaseInsensitive);
+ if (rx.match(host).hasMatch())
return true;
}
}
@@ -292,32 +185,32 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
&& query.queryType() != QNetworkProxyQuery::TcpServer
&& query.queryType() != QNetworkProxyQuery::SctpServer;
for (const QString &entry : proxyList) {
- int server = 0;
+ qsizetype server = 0;
QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
quint16 port = 8080;
- int pos = entry.indexOf(QLatin1Char('='));
- QStringRef scheme;
- QStringRef protocolTag;
+ qsizetype pos = entry.indexOf(u'=');
+ QStringView scheme;
+ QStringView protocolTag;
if (pos != -1) {
- scheme = protocolTag = entry.leftRef(pos);
+ scheme = protocolTag = QStringView{entry}.left(pos);
server = pos + 1;
}
- pos = entry.indexOf(QLatin1String("://"), server);
+ pos = entry.indexOf("://"_L1, server);
if (pos != -1) {
- scheme = entry.midRef(server, pos - server);
+ scheme = QStringView{entry}.mid(server, pos - server);
server = pos + 3;
}
if (!scheme.isEmpty()) {
- if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
+ if (scheme == "http"_L1 || scheme == "https"_L1) {
// no-op
// defaults are above
- } else if (scheme == QLatin1String("socks") || scheme == QLatin1String("socks5")) {
+ } else if (scheme == "socks"_L1 || scheme == "socks5"_L1) {
proxyType = QNetworkProxy::Socks5Proxy;
port = 1080;
- } else if (scheme == QLatin1String("ftp")) {
+ } else if (scheme == "ftp"_L1) {
proxyType = QNetworkProxy::FtpCachingProxy;
port = 2121;
} else {
@@ -326,10 +219,10 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
}
}
- pos = entry.indexOf(QLatin1Char(':'), server);
+ pos = entry.indexOf(u':', server);
if (pos != -1) {
bool ok;
- uint value = entry.midRef(pos + 1).toUInt(&ok);
+ uint value = QStringView{entry}.mid(pos + 1).toUInt(&ok);
if (!ok || value > 65535)
continue; // invalid port number
@@ -352,10 +245,10 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
result.prepend(taggedProxies.value(requiredTag));
}
}
- if (!checkTags || requiredTag != QLatin1String("http")) {
+ if (!checkTags || requiredTag != "http"_L1) {
// if there are different http proxies for http and https, prefer the https one (more likely to be capable of CONNECT)
- QNetworkProxy httpProxy = taggedProxies.value(QLatin1String("http"));
- QNetworkProxy httpsProxy = taggedProxies.value(QLatin1String("http"));
+ QNetworkProxy httpProxy = taggedProxies.value("http"_L1);
+ QNetworkProxy httpsProxy = taggedProxies.value("http"_L1);
if (httpProxy != httpsProxy && httpProxy.type() == QNetworkProxy::HttpProxy && httpsProxy.type() == QNetworkProxy::HttpProxy) {
for (int i = 0; i < result.count(); i++) {
if (httpProxy == result.at(i))
@@ -367,7 +260,6 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
return removeDuplicateProxies(result);
}
-#if !defined(Q_OS_WINRT)
namespace {
class QRegistryWatcher {
Q_DISABLE_COPY_MOVE(QRegistryWatcher)
@@ -403,9 +295,9 @@ public:
}
void clear() {
- for (HANDLE event : qAsConst(m_watchEvents))
+ for (HANDLE event : std::as_const(m_watchEvents))
CloseHandle(event);
- for (HKEY key : qAsConst(m_registryHandles))
+ for (HKEY key : std::as_const(m_registryHandles))
RegCloseKey(key);
m_watchEvents.clear();
@@ -417,11 +309,10 @@ public:
}
private:
- QVector<HANDLE> m_watchEvents;
- QVector<HKEY> m_registryHandles;
+ QList<HANDLE> m_watchEvents;
+ QList<HKEY> m_registryHandles;
};
} // namespace
-#endif // !defined(Q_OS_WINRT)
class QWindowsSystemProxy
{
@@ -441,9 +332,7 @@ public:
QStringList proxyServerList;
QStringList proxyBypass;
QList<QNetworkProxy> defaultResult;
-#if !defined(Q_OS_WINRT)
QRegistryWatcher proxySettingsWatcher;
-#endif
bool initialized;
bool functional;
bool isAutoConfig;
@@ -460,7 +349,7 @@ QWindowsSystemProxy::QWindowsSystemProxy()
QWindowsSystemProxy::~QWindowsSystemProxy()
{
if (hHttpSession)
- ptrWinHttpCloseHandle(hHttpSession);
+ WinHttpCloseHandle(hHttpSession);
}
void QWindowsSystemProxy::reset()
@@ -477,9 +366,7 @@ void QWindowsSystemProxy::reset()
void QWindowsSystemProxy::init()
{
bool proxySettingsChanged = false;
-#if !defined(Q_OS_WINRT)
proxySettingsChanged = proxySettingsWatcher.hasChanged();
-#endif
if (initialized && !proxySettingsChanged)
return;
@@ -487,27 +374,14 @@ void QWindowsSystemProxy::init()
reset();
-#if !defined(Q_OS_WINRT)
proxySettingsWatcher.clear(); // needs reset to trigger a new detection
proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
-#endif
-
- // load the winhttp.dll library
- QSystemLibrary lib(L"winhttp");
- if (!lib.load())
- return; // failed to load
-
- ptrWinHttpOpen = (PtrWinHttpOpen)lib.resolve("WinHttpOpen");
- ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)lib.resolve("WinHttpCloseHandle");
- ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)lib.resolve("WinHttpGetProxyForUrl");
- ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)lib.resolve("WinHttpGetDefaultProxyConfiguration");
- ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)lib.resolve("WinHttpGetIEProxyConfigForCurrentUser");
// Try to obtain the Internet Explorer configuration.
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig;
- const bool hasIEConfig = ptrWinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig);
+ const bool hasIEConfig = WinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig);
if (hasIEConfig) {
if (ieProxyConfig.lpszAutoConfigUrl) {
autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl);
@@ -532,7 +406,7 @@ void QWindowsSystemProxy::init()
// attempt to get the default configuration instead
// that config will serve as default if WPAD fails
WINHTTP_PROXY_INFO proxyInfo;
- if (ptrWinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
+ if (WinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
// we got information from the registry
// overwrite the IE configuration, if any
@@ -550,11 +424,11 @@ void QWindowsSystemProxy::init()
hHttpSession = NULL;
if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) {
// open the handle and obtain the options
- hHttpSession = ptrWinHttpOpen(L"Qt System Proxy access/1.0",
- WINHTTP_ACCESS_TYPE_NO_PROXY,
- WINHTTP_NO_PROXY_NAME,
- WINHTTP_NO_PROXY_BYPASS,
- 0);
+ hHttpSession = WinHttpOpen(L"Qt System Proxy access/1.0",
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
if (!hHttpSession)
return;
@@ -596,11 +470,11 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
// url could be empty, e.g. from QNetworkProxy::applicationProxy(), that's fine,
// we'll still ask for the proxy.
// But for a file url, we know we don't need one.
- if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))
+ if (url.scheme() == "file"_L1 || url.scheme() == "qrc"_L1)
return sp->defaultResult;
if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
// change the scheme to https, maybe it'll work
- url.setScheme(QLatin1String("https"));
+ url.setScheme("https"_L1);
}
QString urlQueryString = url.toString();
@@ -611,7 +485,7 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
urlQueryString = url.toString().left(2083);
}
- bool getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
+ bool getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
&sp->autoProxyOptions,
&proxyInfo);
@@ -629,7 +503,7 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
sp->autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
sp->autoProxyOptions.lpszAutoConfigUrl =
reinterpret_cast<LPCWSTR>(sp->autoConfigUrl.utf16());
- getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
+ getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
&sp->autoProxyOptions,
&proxyInfo);
@@ -642,7 +516,7 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
// We first tried without AutoLogon, because this might prevent caching the result.
// But now we've to enable it (http://msdn.microsoft.com/en-us/library/aa383153%28v=VS.85%29.aspx)
sp->autoProxyOptions.fAutoLogonIfChallenged = TRUE;
- getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
+ getProxySucceeded = WinHttpGetProxyForUrl(sp->hHttpSession,
reinterpret_cast<LPCWSTR>(urlQueryString.utf16()),
&sp->autoProxyOptions,
&proxyInfo);
diff --git a/src/network/kernel/qtldurl.cpp b/src/network/kernel/qtldurl.cpp
new file mode 100644
index 0000000000..a7aceddb18
--- /dev/null
+++ b/src/network/kernel/qtldurl.cpp
@@ -0,0 +1,215 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qglobal.h>
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#if QT_CONFIG(topleveldomain)
+
+#include "QtCore/qfile.h"
+#include "QtCore/qloggingcategory.h"
+#include "QtCore/qstandardpaths.h"
+#include "QtCore/qstring.h"
+
+#if !QT_CONFIG(publicsuffix_qt) && !QT_CONFIG(publicsuffix_system)
+# error Enable at least one feature: publicsuffix-qt, publicsuffix-system
+#endif
+
+#if QT_CONFIG(publicsuffix_qt)
+# include "psl_data.cpp"
+#endif
+
+// Defined in src/3rdparty/libpsl/src/lookup_string_in_fixed_set.c
+extern "C" int LookupStringInFixedSet(const unsigned char *graph, std::size_t length,
+ const char *key, std::size_t key_length);
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcTld, "qt.network.tld")
+
+static constexpr int PSL_NOT_FOUND = -1;
+static constexpr int PSL_FLAG_EXCEPTION = 1 << 0;
+static constexpr int PSL_FLAG_WILDCARD = 1 << 1;
+
+class QPublicSuffixDatabase final
+{
+public:
+#if QT_CONFIG(publicsuffix_system)
+ QPublicSuffixDatabase();
+#endif // QT_CONFIG(publicsuffix_system)
+
+ int lookupDomain(QByteArrayView domain) const;
+
+private:
+ QByteArrayView m_data
+#if QT_CONFIG(publicsuffix_qt)
+ {
+ kDafsa, sizeof(kDafsa)
+ }
+#endif // QT_CONFIG(publicsuffix_qt)
+ ;
+
+#if QT_CONFIG(publicsuffix_system)
+ std::unique_ptr<QFile> m_dev;
+ QByteArray m_storage;
+ bool loadFile(const QString &fileName);
+#endif // QT_CONFIG(publicsuffix_system)
+};
+
+int QPublicSuffixDatabase::lookupDomain(QByteArrayView domain) const
+{
+ return LookupStringInFixedSet(reinterpret_cast<const unsigned char *>(m_data.constData()),
+ m_data.size(), domain.data(), domain.size());
+}
+
+#if QT_CONFIG(publicsuffix_system)
+
+static QStringList locatePublicSuffixFiles()
+{
+ return QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
+ u"publicsuffix/public_suffix_list.dafsa"_s);
+}
+
+QPublicSuffixDatabase::QPublicSuffixDatabase()
+{
+ for (auto &&fileName : locatePublicSuffixFiles()) {
+ if (loadFile(fileName))
+ return;
+ }
+
+#if QT_CONFIG(publicsuffix_qt)
+ qCDebug(lcTld, "Using builtin publicsuffix list");
+#else
+ qCWarning(lcTld, "No usable publicsuffix file found");
+#endif
+}
+
+bool QPublicSuffixDatabase::loadFile(const QString &fileName)
+{
+ static const QByteArrayView DafsaFileHeader = ".DAFSA@PSL_0 \n";
+
+ qCDebug(lcTld, "Loading publicsuffix file: %s", qUtf8Printable(fileName));
+
+ auto systemFile = std::make_unique<QFile>(fileName);
+
+ if (!systemFile->open(QIODevice::ReadOnly)) {
+ qCDebug(lcTld, "Failed to open publicsuffix file: %s",
+ qUtf8Printable(systemFile->errorString()));
+ return false;
+ }
+
+ auto fileSize = systemFile->size();
+ // Check if there is enough data for header, version byte and some data
+ if (fileSize < DafsaFileHeader.size() + 2) {
+ qCWarning(lcTld, "publicsuffix file is too small: %zu", std::size_t(fileSize));
+ return false;
+ }
+
+ auto header = systemFile->read(DafsaFileHeader.size());
+ if (header != DafsaFileHeader) {
+ qCWarning(lcTld, "Invalid publicsuffix file header: %s", header.toHex().constData());
+ return false;
+ }
+
+ // Check if the file is UTF-8 compatible
+ if (!systemFile->seek(fileSize - 1)) {
+ qCWarning(lcTld, "Failed to seek to the end of file: %s",
+ qUtf8Printable(systemFile->errorString()));
+ return false;
+ }
+
+ char version;
+ if (systemFile->read(&version, 1) != 1) {
+ qCWarning(lcTld, "Failed to read publicsuffix version");
+ return false;
+ }
+
+ if (version != 0x01) {
+ qCWarning(lcTld, "Unsupported publicsuffix version: %d", int(version));
+ return false;
+ }
+
+ const auto dataSize = fileSize - DafsaFileHeader.size() - 1;
+ // Try to map the file first
+ auto mappedData = systemFile->map(DafsaFileHeader.size(), dataSize);
+ if (mappedData) {
+ qCDebug(lcTld, "Using mapped system publicsuffix data");
+ systemFile->close();
+ m_data = QByteArrayView(mappedData, dataSize);
+ m_dev = std::move(systemFile);
+ return true;
+ }
+
+ qCDebug(lcTld, "Failed to map publicsuffix file: %s",
+ qUtf8Printable(systemFile->errorString()));
+
+ systemFile->seek(DafsaFileHeader.size());
+ m_storage = systemFile->read(dataSize);
+ if (m_storage.size() != dataSize) {
+ qCWarning(lcTld, "Failed to read publicsuffix file");
+ m_storage.clear();
+ return false;
+ }
+
+ qCDebug(lcTld, "Using system publicsuffix data");
+ m_data = m_storage;
+
+ return true;
+}
+
+Q_GLOBAL_STATIC(QPublicSuffixDatabase, publicSuffix);
+
+#else
+
+static const QPublicSuffixDatabase m_publicSuffix;
+
+#endif // QT_CONFIG(publicsuffix_system)
+
+/*!
+ \internal
+
+ Return true if \a domain is a top-level-domain per Qt's copy of the Mozilla public suffix list.
+
+ The \a domain must be in lower-case format (as per QString::toLower()).
+*/
+
+Q_NETWORK_EXPORT bool qIsEffectiveTLD(QStringView domain)
+{
+ // for domain 'foo.bar.com':
+ // 1. return false if TLD table contains '!foo.bar.com'
+ // 2. return true if TLD table contains 'foo.bar.com'
+ // 3. return true if the table contains '*.bar.com'
+
+ QByteArray decodedDomain = domain.toUtf8();
+ QByteArrayView domainView(decodedDomain);
+
+#if QT_CONFIG(publicsuffix_system)
+ if (publicSuffix.isDestroyed())
+ return false;
+#else
+ auto publicSuffix = &m_publicSuffix;
+#endif // QT_CONFIG(publicsuffix_system)
+
+ auto ret = publicSuffix->lookupDomain(domainView);
+ if (ret != PSL_NOT_FOUND) {
+ if (ret & PSL_FLAG_EXCEPTION) // 1
+ return false;
+ if ((ret & PSL_FLAG_WILDCARD) == 0) // 2
+ return true;
+ }
+
+ const auto dot = domainView.indexOf('.');
+ if (dot < 0) // Actual TLD: may be effective if the subject of a wildcard rule:
+ return ret != PSL_NOT_FOUND;
+ ret = publicSuffix->lookupDomain(domainView.sliced(dot + 1)); // 3
+ if (ret == PSL_NOT_FOUND)
+ return false;
+ return (ret & PSL_FLAG_WILDCARD) != 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(topleveldomain)
diff --git a/src/network/kernel/qtldurl_p.h b/src/network/kernel/qtldurl_p.h
new file mode 100644
index 0000000000..86b163f161
--- /dev/null
+++ b/src/network/kernel/qtldurl_p.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTLDURL_P_H
+#define QTLDURL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qDecodeDataUrl. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include "QtCore/qstring.h"
+
+QT_REQUIRE_CONFIG(topleveldomain);
+
+QT_BEGIN_NAMESPACE
+
+Q_NETWORK_EXPORT bool qIsEffectiveTLD(QStringView domain);
+inline bool qIsEffectiveTLD(const QString &domain)
+{
+ return qIsEffectiveTLD(qToStringViewIgnoringNull(domain));
+}
+
+QT_END_NAMESPACE
+
+#endif // QTLDURL_P_H
diff --git a/src/network/kernel/qtnetworkglobal.h b/src/network/kernel/qtnetworkglobal.h
index 586b847816..b22ddc6950 100644
--- a/src/network/kernel/qtnetworkglobal.h
+++ b/src/network/kernel/qtnetworkglobal.h
@@ -1,61 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTNETWORKGLOBAL_H
#define QTNETWORKGLOBAL_H
#include <QtCore/qglobal.h>
#include <QtNetwork/qtnetwork-config.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_STATIC
-# if defined(QT_BUILD_NETWORK_LIB)
-# define Q_NETWORK_EXPORT Q_DECL_EXPORT
-# else
-# define Q_NETWORK_EXPORT Q_DECL_IMPORT
-# endif
-#else
-# define Q_NETWORK_EXPORT
-#endif
-
-QT_END_NAMESPACE
+#include <QtNetwork/qtnetworkexports.h>
#endif
diff --git a/src/network/kernel/qtnetworkglobal_p.h b/src/network/kernel/qtnetworkglobal_p.h
index 859e3d9ebd..b90e675cb4 100644
--- a/src/network/kernel/qtnetworkglobal_p.h
+++ b/src/network/kernel/qtnetworkglobal_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTNETWORKGLOBAL_P_H
#define QTNETWORKGLOBAL_P_H
@@ -55,4 +19,15 @@
#include <QtCore/private/qglobal_p.h>
#include <QtNetwork/private/qtnetwork-config_p.h>
+QT_BEGIN_NAMESPACE
+
+enum {
+#if defined(Q_OS_LINUX) || defined(Q_OS_QNX)
+ PlatformSupportsAbstractNamespace = true
+#else
+ PlatformSupportsAbstractNamespace = false
+#endif
+};
+
+QT_END_NAMESPACE
#endif // QTNETWORKGLOBAL_P_H
diff --git a/src/network/kernel/qurlinfo.cpp b/src/network/kernel/qurlinfo.cpp
deleted file mode 100644
index e6f2e70ff4..0000000000
--- a/src/network/kernel/qurlinfo.cpp
+++ /dev/null
@@ -1,727 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qurlinfo_p.h"
-
-#include "qurl.h"
-#include "qdir.h"
-#include <limits.h>
-
-QT_BEGIN_NAMESPACE
-
-class QUrlInfoPrivate
-{
-public:
- QUrlInfoPrivate() :
- permissions(0),
- size(0),
- isDir(false),
- isFile(true),
- isSymLink(false),
- isWritable(true),
- isReadable(true),
- isExecutable(false)
- {}
-
- QString name;
- int permissions;
- QString owner;
- QString group;
- qint64 size;
-
- QDateTime lastModified;
- QDateTime lastRead;
- bool isDir;
- bool isFile;
- bool isSymLink;
- bool isWritable;
- bool isReadable;
- bool isExecutable;
-};
-
-
-/*!
- \class QUrlInfo
- \brief The QUrlInfo class stores information about URLs.
-
- \internal
- \ingroup io
- \ingroup network
- \inmodule QtNetwork
-
- The information about a URL that can be retrieved includes name(),
- permissions(), owner(), group(), size(), lastModified(),
- lastRead(), isDir(), isFile(), isSymLink(), isWritable(),
- isReadable() and isExecutable().
-
- You can create your own QUrlInfo objects passing in all the
- relevant information in the constructor, and you can modify a
- QUrlInfo; for each getter mentioned above there is an equivalent
- setter. Note that setting values does not affect the underlying
- resource that the QUrlInfo provides information about; for example
- if you call setWritable(true) on a read-only resource the only
- thing changed is the QUrlInfo object, not the resource.
-
- \sa QUrl, {FTP Example}
-*/
-
-/*!
- \enum QUrlInfo::PermissionSpec
-
- This enum is used by the permissions() function to report the
- permissions of a file.
-
- \value ReadOwner The file is readable by the owner of the file.
- \value WriteOwner The file is writable by the owner of the file.
- \value ExeOwner The file is executable by the owner of the file.
- \value ReadGroup The file is readable by the group.
- \value WriteGroup The file is writable by the group.
- \value ExeGroup The file is executable by the group.
- \value ReadOther The file is readable by anyone.
- \value WriteOther The file is writable by anyone.
- \value ExeOther The file is executable by anyone.
-*/
-
-/*!
- Constructs an invalid QUrlInfo object with default values.
-
- \sa isValid()
-*/
-
-QUrlInfo::QUrlInfo()
-{
- d = nullptr;
-}
-
-/*!
- Copy constructor, copies \a ui to this URL info object.
-*/
-
-QUrlInfo::QUrlInfo(const QUrlInfo &ui)
-{
- if (ui.d) {
- d = new QUrlInfoPrivate;
- *d = *ui.d;
- } else {
- d = nullptr;
- }
-}
-
-/*!
- Constructs a QUrlInfo object by specifying all the URL's
- information.
-
- The information that is passed is the \a name, file \a
- permissions, \a owner and \a group and the file's \a size. Also
- passed is the \a lastModified date/time and the \a lastRead
- date/time. Flags are also passed, specifically, \a isDir, \a
- isFile, \a isSymLink, \a isWritable, \a isReadable and \a
- isExecutable.
-*/
-
-QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner,
- const QString &group, qint64 size, const QDateTime &lastModified,
- const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
- bool isWritable, bool isReadable, bool isExecutable)
-{
- d = new QUrlInfoPrivate;
- d->name = name;
- d->permissions = permissions;
- d->owner = owner;
- d->group = group;
- d->size = size;
- d->lastModified = lastModified;
- d->lastRead = lastRead;
- d->isDir = isDir;
- d->isFile = isFile;
- d->isSymLink = isSymLink;
- d->isWritable = isWritable;
- d->isReadable = isReadable;
- d->isExecutable = isExecutable;
-}
-
-
-/*!
- Constructs a QUrlInfo object by specifying all the URL's
- information.
-
- The information that is passed is the \a url, file \a
- permissions, \a owner and \a group and the file's \a size. Also
- passed is the \a lastModified date/time and the \a lastRead
- date/time. Flags are also passed, specifically, \a isDir, \a
- isFile, \a isSymLink, \a isWritable, \a isReadable and \a
- isExecutable.
-*/
-
-QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner,
- const QString &group, qint64 size, const QDateTime &lastModified,
- const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
- bool isWritable, bool isReadable, bool isExecutable)
-{
- d = new QUrlInfoPrivate;
- d->name = QFileInfo(url.path()).fileName();
- d->permissions = permissions;
- d->owner = owner;
- d->group = group;
- d->size = size;
- d->lastModified = lastModified;
- d->lastRead = lastRead;
- d->isDir = isDir;
- d->isFile = isFile;
- d->isSymLink = isSymLink;
- d->isWritable = isWritable;
- d->isReadable = isReadable;
- d->isExecutable = isExecutable;
-}
-
-
-/*!
- Sets the name of the URL to \a name. The name is the full text,
- for example, "http://qt-project.org/doc/qt-5.0/qtcore/qurl.html".
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setName(const QString &name)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->name = name;
-}
-
-
-/*!
- If \a b is true then the URL is set to be a directory; if \a b is
- false then the URL is set not to be a directory (which normally
- means it is a file). (Note that a URL can refer to both a file and
- a directory even though most file systems do not support this.)
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setDir(bool b)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->isDir = b;
-}
-
-
-/*!
- If \a b is true then the URL is set to be a file; if \b is false
- then the URL is set not to be a file (which normally means it is a
- directory). (Note that a URL can refer to both a file and a
- directory even though most file systems do not support this.)
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setFile(bool b)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->isFile = b;
-}
-
-
-/*!
- Specifies that the URL refers to a symbolic link if \a b is true
- and that it does not if \a b is false.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setSymLink(bool b)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->isSymLink = b;
-}
-
-
-/*!
- Specifies that the URL is writable if \a b is true and not
- writable if \a b is false.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setWritable(bool b)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->isWritable = b;
-}
-
-
-/*!
- Specifies that the URL is readable if \a b is true and not
- readable if \a b is false.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setReadable(bool b)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->isReadable = b;
-}
-
-/*!
- Specifies that the owner of the URL is called \a s.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setOwner(const QString &s)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->owner = s;
-}
-
-/*!
- Specifies that the owning group of the URL is called \a s.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setGroup(const QString &s)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->group = s;
-}
-
-/*!
- Specifies the \a size of the URL.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setSize(qint64 size)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->size = size;
-}
-
-/*!
- Specifies that the URL has access permissions \a p.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setPermissions(int p)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->permissions = p;
-}
-
-/*!
- Specifies that the object the URL refers to was last modified at
- \a dt.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setLastModified(const QDateTime &dt)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->lastModified = dt;
-}
-
-/*!
- \since 4.4
-
- Specifies that the object the URL refers to was last read at
- \a dt.
-
- If you call this function for an invalid URL info, this function
- turns it into a valid one.
-
- \sa isValid()
-*/
-
-void QUrlInfo::setLastRead(const QDateTime &dt)
-{
- if (!d)
- d = new QUrlInfoPrivate;
- d->lastRead = dt;
-}
-
-/*!
- Destroys the URL info object.
-*/
-
-QUrlInfo::~QUrlInfo()
-{
- delete d;
-}
-
-/*!
- Assigns the values of \a ui to this QUrlInfo object.
-*/
-
-QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui)
-{
- if (ui.d) {
- if (!d)
- d= new QUrlInfoPrivate;
- *d = *ui.d;
- } else {
- delete d;
- d = nullptr;
- }
- return *this;
-}
-
-/*!
- Returns the file name of the URL.
-
- \sa isValid()
-*/
-
-QString QUrlInfo::name() const
-{
- if (!d)
- return QString();
- return d->name;
-}
-
-/*!
- Returns the permissions of the URL. You can use the \c PermissionSpec flags
- to test for certain permissions.
-
- \sa isValid()
-*/
-
-int QUrlInfo::permissions() const
-{
- if (!d)
- return 0;
- return d->permissions;
-}
-
-/*!
- Returns the owner of the URL.
-
- \sa isValid()
-*/
-
-QString QUrlInfo::owner() const
-{
- if (!d)
- return QString();
- return d->owner;
-}
-
-/*!
- Returns the group of the URL.
-
- \sa isValid()
-*/
-
-QString QUrlInfo::group() const
-{
- if (!d)
- return QString();
- return d->group;
-}
-
-/*!
- Returns the size of the URL.
-
- \sa isValid()
-*/
-
-qint64 QUrlInfo::size() const
-{
- if (!d)
- return 0;
- return d->size;
-}
-
-/*!
- Returns the last modification date of the URL.
-
- \sa isValid()
-*/
-
-QDateTime QUrlInfo::lastModified() const
-{
- if (!d)
- return QDateTime();
- return d->lastModified;
-}
-
-/*!
- Returns the date when the URL was last read.
-
- \sa isValid()
-*/
-
-QDateTime QUrlInfo::lastRead() const
-{
- if (!d)
- return QDateTime();
- return d->lastRead;
-}
-
-/*!
- Returns \c true if the URL is a directory; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isDir() const
-{
- if (!d)
- return false;
- return d->isDir;
-}
-
-/*!
- Returns \c true if the URL is a file; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isFile() const
-{
- if (!d)
- return false;
- return d->isFile;
-}
-
-/*!
- Returns \c true if the URL is a symbolic link; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isSymLink() const
-{
- if (!d)
- return false;
- return d->isSymLink;
-}
-
-/*!
- Returns \c true if the URL is writable; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isWritable() const
-{
- if (!d)
- return false;
- return d->isWritable;
-}
-
-/*!
- Returns \c true if the URL is readable; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isReadable() const
-{
- if (!d)
- return false;
- return d->isReadable;
-}
-
-/*!
- Returns \c true if the URL is executable; otherwise returns \c false.
-
- \sa isValid()
-*/
-
-bool QUrlInfo::isExecutable() const
-{
- if (!d)
- return false;
- return d->isExecutable;
-}
-
-/*!
- Returns \c true if \a i1 is greater than \a i2; otherwise returns
- false. The objects are compared by the value, which is specified
- by \a sortBy. This must be one of QDir::Name, QDir::Time or
- QDir::Size.
-*/
-
-bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy)
-{
- switch (sortBy) {
- case QDir::Name:
- return i1.name() > i2.name();
- case QDir::Time:
- return i1.lastModified() > i2.lastModified();
- case QDir::Size:
- return i1.size() > i2.size();
- default:
- return false;
- }
-}
-
-/*!
- Returns \c true if \a i1 is less than \a i2; otherwise returns \c false.
- The objects are compared by the value, which is specified by \a
- sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
-*/
-
-bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy)
-{
- return !greaterThan(i1, i2, sortBy);
-}
-
-/*!
- Returns \c true if \a i1 equals to \a i2; otherwise returns \c false.
- The objects are compared by the value, which is specified by \a
- sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
-*/
-
-bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy)
-{
- switch (sortBy) {
- case QDir::Name:
- return i1.name() == i2.name();
- case QDir::Time:
- return i1.lastModified() == i2.lastModified();
- case QDir::Size:
- return i1.size() == i2.size();
- default:
- return false;
- }
-}
-
-/*!
- Returns \c true if this QUrlInfo is equal to \a other; otherwise
- returns \c false.
-
- \sa lessThan(), equal()
-*/
-
-bool QUrlInfo::operator==(const QUrlInfo &other) const
-{
- if (!d)
- return other.d == nullptr;
- if (!other.d)
- return false;
-
- return (d->name == other.d->name &&
- d->permissions == other.d->permissions &&
- d->owner == other.d->owner &&
- d->group == other.d->group &&
- d->size == other.d->size &&
- d->lastModified == other.d->lastModified &&
- d->lastRead == other.d->lastRead &&
- d->isDir == other.d->isDir &&
- d->isFile == other.d->isFile &&
- d->isSymLink == other.d->isSymLink &&
- d->isWritable == other.d->isWritable &&
- d->isReadable == other.d->isReadable &&
- d->isExecutable == other.d->isExecutable);
-}
-
-/*!
- \fn bool QUrlInfo::operator!=(const QUrlInfo &other) const
- \since 4.2
-
- Returns \c true if this QUrlInfo is not equal to \a other; otherwise
- returns \c false.
-
- \sa lessThan(), equal()
-*/
-
-/*!
- Returns \c true if the URL info is valid; otherwise returns \c false.
- Valid means that the QUrlInfo contains real information.
-
- You should always check if the URL info is valid before relying on
- the values.
-*/
-bool QUrlInfo::isValid() const
-{
- return d != nullptr;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/kernel/qurlinfo_p.h b/src/network/kernel/qurlinfo_p.h
deleted file mode 100644
index 8180796f49..0000000000
--- a/src/network/kernel/qurlinfo_p.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QURLINFO_H
-#define QURLINFO_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qiodevice.h>
-
-QT_REQUIRE_CONFIG(ftp);
-
-QT_BEGIN_NAMESPACE
-
-class QUrl;
-class QUrlInfoPrivate;
-
-class Q_NETWORK_EXPORT QUrlInfo
-{
-public:
- enum PermissionSpec {
- ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100,
- ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010,
- ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 };
-
- QUrlInfo();
- QUrlInfo(const QUrlInfo &ui);
- QUrlInfo(const QString &name, int permissions, const QString &owner,
- const QString &group, qint64 size, const QDateTime &lastModified,
- const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
- bool isWritable, bool isReadable, bool isExecutable);
- QUrlInfo(const QUrl &url, int permissions, const QString &owner,
- const QString &group, qint64 size, const QDateTime &lastModified,
- const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
- bool isWritable, bool isReadable, bool isExecutable);
- QUrlInfo &operator=(const QUrlInfo &ui);
- virtual ~QUrlInfo();
-
- virtual void setName(const QString &name);
- virtual void setDir(bool b);
- virtual void setFile(bool b);
- virtual void setSymLink(bool b);
- virtual void setOwner(const QString &s);
- virtual void setGroup(const QString &s);
- virtual void setSize(qint64 size);
- virtual void setWritable(bool b);
- virtual void setReadable(bool b);
- virtual void setPermissions(int p);
- virtual void setLastModified(const QDateTime &dt);
- void setLastRead(const QDateTime &dt);
-
- bool isValid() const;
-
- QString name() const;
- int permissions() const;
- QString owner() const;
- QString group() const;
- qint64 size() const;
- QDateTime lastModified() const;
- QDateTime lastRead() const;
- bool isDir() const;
- bool isFile() const;
- bool isSymLink() const;
- bool isWritable() const;
- bool isReadable() const;
- bool isExecutable() const;
-
- static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy);
- static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy);
- static bool equal(const QUrlInfo &i1, const QUrlInfo &i2,
- int sortBy);
-
- bool operator==(const QUrlInfo &i) const;
- inline bool operator!=(const QUrlInfo &i) const
- { return !operator==(i); }
-
-private:
- QUrlInfoPrivate *d;
-};
-
-QT_END_NAMESPACE
-
-#endif // QURLINFO_H
diff --git a/src/network/network.pro b/src/network/network.pro
deleted file mode 100644
index d8453e879c..0000000000
--- a/src/network/network.pro
+++ /dev/null
@@ -1,43 +0,0 @@
-TARGET = QtNetwork
-QT = core-private
-
-DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH
-#DEFINES += QLOCALSERVER_DEBUG QLOCALSOCKET_DEBUG
-#DEFINES += QNETWORKDISKCACHE_DEBUG
-#DEFINES += QSSLSOCKET_DEBUG
-#DEFINES += QHOSTINFO_DEBUG
-#DEFINES += QABSTRACTSOCKET_DEBUG QNATIVESOCKETENGINE_DEBUG
-#DEFINES += QTCPSOCKETENGINE_DEBUG QTCPSOCKET_DEBUG QTCPSERVER_DEBUG QSSLSOCKET_DEBUG
-#DEFINES += QUDPSOCKET_DEBUG QUDPSERVER_DEBUG
-#DEFINES += QSCTPSOCKET_DEBUG QSCTPSERVER_DEBUG
-msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x64000000
-
-QMAKE_DOCS = $$PWD/doc/qtnetwork.qdocconf
-
-include(access/access.pri)
-include(bearer/bearer.pri)
-include(kernel/kernel.pri)
-include(socket/socket.pri)
-include(ssl/ssl.pri)
-
-QMAKE_LIBS += $$QMAKE_LIBS_NETWORK
-
-qtConfig(bearermanagement) {
- ANDROID_BUNDLED_JAR_DEPENDENCIES = \
- jar/QtAndroidBearer.jar
- ANDROID_LIB_DEPENDENCIES = \
- plugins/bearer/libplugins_bearer_qandroidbearer.so
- MODULE_PLUGIN_TYPES = \
- bearer
- ANDROID_PERMISSIONS += \
- android.permission.ACCESS_NETWORK_STATE
-}
-
-MODULE_WINRT_CAPABILITIES = \
- internetClient \
- internetClientServer \
- privateNetworkClientServer
-
-MODULE_PLUGIN_TYPES = \
- bearer
-load(qt_module)
diff --git a/src/network/qt_cmdline.cmake b/src/network/qt_cmdline.cmake
new file mode 100644
index 0000000000..0b4c0c239a
--- /dev/null
+++ b/src/network/qt_cmdline.cmake
@@ -0,0 +1,12 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_commandline_option(libproxy TYPE boolean)
+qt_commandline_option(dtls TYPE boolean)
+qt_commandline_option(ocsp TYPE boolean)
+qt_commandline_option(sctp TYPE boolean)
+qt_commandline_option(securetransport TYPE boolean)
+qt_commandline_option(schannel TYPE boolean)
+qt_commandline_option(ssl TYPE boolean)
+qt_commandline_option(system-proxies TYPE boolean)
+qt_commandline_option(publicsuffix TYPE optionalString VALUES system qt no all)
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index cbc4114904..e456d00713 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QABSTRACTSOCKET_DEBUG
@@ -84,7 +48,7 @@
HostLookupState. If the host is found, QAbstractSocket enters
ConnectingState and emits the hostFound() signal. When the
connection has been established, it enters ConnectedState and
- emits connected(). If an error occurs at any stage, error() is
+ emits connected(). If an error occurs at any stage, errorOccurred() is
emitted. Whenever the state changes, stateChanged() is emitted.
For convenience, isValid() returns \c true if the socket is ready for
reading and writing, but note that the socket's state must be
@@ -113,7 +77,7 @@
QAbstractSocket::UnconnectedState, and emits disconnected(). If you want
to abort a connection immediately, discarding all pending data, call
abort() instead. If the remote host closes the connection,
- QAbstractSocket will emit error(QAbstractSocket::RemoteHostClosedError),
+ QAbstractSocket will emit errorOccurred(QAbstractSocket::RemoteHostClosedError),
during which the socket state will still be ConnectedState, and then the
disconnected() signal will be emitted.
@@ -202,14 +166,16 @@
*/
/*!
- \fn void QAbstractSocket::error(QAbstractSocket::SocketError socketError)
+ \fn void QAbstractSocket::errorOccurred(QAbstractSocket::SocketError socketError)
+ \since 5.15
This signal is emitted after an error occurred. The \a socketError
parameter describes the type of error that occurred.
When this signal is emitted, the socket may not be ready for a reconnect
- attempt. In that case, attempts to reconnect should be done from the event
- loop. For example, use a QTimer::singleShot() with 0 as the timeout.
+ attempt. In that case, attempts to reconnect should be done from the
+ event loop. For example, use QChronoTimer::singleShot() with 0ns as
+ the timeout.
QAbstractSocket::SocketError is not a registered metatype, so for queued
connections, you will have to register it with Q_DECLARE_METATYPE() and
@@ -330,6 +296,7 @@
\value UnknownSocketError An unidentified error occurred.
\sa QAbstractSocket::error()
+ \sa QAbstractSocket::errorOccurred()
*/
/*!
@@ -428,7 +395,7 @@
\value DontShareAddress Bind the address and port exclusively, so that
no other services are allowed to rebind. By passing this option to
- QAbstractSocket::bind(), you are guaranteed that on successs, your service
+ QAbstractSocket::bind(), you are guaranteed that on success, your service
is the only one that listens to the address and port. No services are
allowed to rebind, even if they pass ReuseAddressHint. This option
provides more security than ShareAddress, but on certain operating
@@ -460,11 +427,12 @@
SSL error notification. I.E. QSslSocket::sslErrors().
*/
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#include "qabstractsocket.h"
#include "qabstractsocket_p.h"
#include "private/qhostinfo_p.h"
-#include "private/qnetworksession_p.h"
#include <qabstracteventdispatcher.h>
#include <qhostaddress.h>
@@ -472,18 +440,15 @@
#include <qmetaobject.h>
#include <qpointer.h>
#include <qtimer.h>
-#include <qelapsedtimer.h>
+#include <qdeadlinetimer.h>
#include <qscopedvaluerollback.h>
#include <qvarlengtharray.h>
-#ifndef QT_NO_SSL
-#include <QtNetwork/qsslsocket.h>
-#endif
-
#include <private/qthread_p.h>
#ifdef QABSTRACTSOCKET_DEBUG
#include <qdebug.h>
+#include <private/qdebug_p.h>
#endif
#include <time.h>
@@ -500,41 +465,13 @@
QT_BEGIN_NAMESPACE
-#if defined QABSTRACTSOCKET_DEBUG
-QT_BEGIN_INCLUDE_NAMESPACE
-#include <qstring.h>
-#include <ctype.h>
-QT_END_INCLUDE_NAMESPACE
-
-/*
- Returns a human readable representation of the first \a len
- characters in \a data.
-*/
-static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
-{
- if (!data) return "(null)";
- QByteArray out;
- for (int i = 0; i < qMin(len, maxLength); ++i) {
- char c = data[i];
- if (isprint(int(uchar(c)))) {
- out += c;
- } else switch (c) {
- case '\n': out += "\\n"; break;
- case '\r': out += "\\r"; break;
- case '\t': out += "\\t"; break;
- default:
- QString tmp;
- tmp.sprintf("\\%o", c);
- out += tmp.toLatin1();
- }
- }
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
- if (len < maxLength)
- out += "...";
+QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketState, QAbstractSocket__SocketState)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketError, QAbstractSocket__SocketError)
- return out;
-}
-#endif
+static constexpr auto DefaultConnectTimeout = 30s;
static bool isProxyError(QAbstractSocket::SocketError error)
{
@@ -556,25 +493,6 @@ static bool isProxyError(QAbstractSocket::SocketError error)
Constructs a QAbstractSocketPrivate. Initializes all members.
*/
QAbstractSocketPrivate::QAbstractSocketPrivate()
- : emittedReadyRead(false),
- emittedBytesWritten(false),
- abortCalled(false),
- pendingClose(false),
- pauseMode(QAbstractSocket::PauseNever),
- port(0),
- localPort(0),
- peerPort(0),
- socketEngine(nullptr),
- cachedSocketDescriptor(-1),
- readBufferMaxSize(0),
- isBuffered(false),
- hasPendingData(false),
- connectTimer(nullptr),
- hostLookupId(-1),
- socketType(QAbstractSocket::UnknownSocketType),
- state(QAbstractSocket::UnconnectedState),
- socketError(QAbstractSocket::UnknownSocketError),
- preferredNetworkLayerProtocol(QAbstractSocket::UnknownNetworkLayerProtocol)
{
writeBufferChunkSize = QABSTRACTSOCKET_BUFFERSIZE;
}
@@ -626,14 +544,14 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc
Q_Q(QAbstractSocket);
#if defined (QABSTRACTSOCKET_DEBUG)
QString typeStr;
- if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
- else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
- else if (q->socketType() == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket");
- else typeStr = QLatin1String("UnknownSocketType");
+ if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = "TcpSocket"_L1;
+ else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = "UdpSocket"_L1;
+ else if (q->socketType() == QAbstractSocket::SctpSocket) typeStr = "SctpSocket"_L1;
+ else typeStr = "UnknownSocketType"_L1;
QString protocolStr;
- if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
- else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
- else protocolStr = QLatin1String("UnknownNetworkLayerProtocol");
+ if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = "IPv4Protocol"_L1;
+ else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = "IPv6Protocol"_L1;
+ else protocolStr = "UnknownNetworkLayerProtocol"_L1;
#endif
resetSocketLayer();
@@ -643,10 +561,6 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc
QAbstractSocket::tr("Operation on socket is not supported"));
return false;
}
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket engine (if it has been set)
- socketEngine->setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
if (!socketEngine->initialize(q->socketType(), protocol)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::initSocketLayer(%s, %s) failed (%s)",
@@ -724,11 +638,19 @@ bool QAbstractSocketPrivate::canReadNotification()
return !q->isReadable();
}
} else {
- if (hasPendingData) {
+ const bool isUdpSocket = (socketType == QAbstractSocket::UdpSocket);
+ if (hasPendingData && (!isUdpSocket || hasPendingDatagram)) {
socketEngine->setReadNotificationEnabled(false);
return true;
}
- hasPendingData = true;
+ if (!isUdpSocket
+#if QT_CONFIG(udpsocket)
+ || socketEngine->hasPendingDatagrams()
+#endif
+ ) {
+ hasPendingData = true;
+ hasPendingDatagram = isUdpSocket;
+ }
}
emitReadyRead();
@@ -924,7 +846,7 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
}
// return the first that we can use
- for (const QNetworkProxy &p : qAsConst(proxies)) {
+ for (const QNetworkProxy &p : std::as_const(proxies)) {
if (socketType == QAbstractSocket::UdpSocket &&
(p.capabilities() & QNetworkProxy::UdpTunnelingCapability) == 0)
continue;
@@ -947,7 +869,7 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
}
#endif // !QT_NO_NETWORKPROXY
-#if !defined(QT_NO_NETWORKPROXY) || defined(Q_OS_WINRT)
+#if !defined(QT_NO_NETWORKPROXY)
/*!
\internal
@@ -985,11 +907,11 @@ void QAbstractSocketPrivate::startConnectingByName(const QString &host)
}
state = QAbstractSocket::UnconnectedState;
- emit q->error(socketError);
+ emit q->errorOccurred(socketError);
emit q->stateChanged(state);
}
-#endif // !QT_NO_NETWORKPROXY || Q_OS_WINRT
+#endif // !QT_NO_NETWORKPROXY
/*! \internal
@@ -1023,12 +945,12 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
#if defined(QABSTRACTSOCKET_DEBUG)
- QString s = QLatin1String("{");
+ QString s = "{"_L1;
for (int i = 0; i < addresses.count(); ++i) {
- if (i != 0) s += QLatin1String(", ");
+ if (i != 0) s += ", "_L1;
s += addresses.at(i).toString();
}
- s += QLatin1Char('}');
+ s += u'}';
qDebug("QAbstractSocketPrivate::_q_startConnecting(hostInfo == %s)", s.toLatin1().constData());
#endif
@@ -1044,7 +966,7 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
state = QAbstractSocket::UnconnectedState;
setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
emit q->stateChanged(state);
- emit q->error(QAbstractSocket::HostNotFoundError);
+ emit q->errorOccurred(QAbstractSocket::HostNotFoundError);
return;
}
@@ -1067,8 +989,8 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
_q_testConnection(), this function takes the first address of the
pending addresses list and tries to connect to it. If the
connection succeeds, QAbstractSocket will emit
- connected(). Otherwise, error(ConnectionRefusedError) or
- error(SocketTimeoutError) is emitted.
+ connected(). Otherwise, errorOccurred(ConnectionRefusedError) or
+ errorOccurred(SocketTimeoutError) is emitted.
*/
void QAbstractSocketPrivate::_q_connectToNextAddress()
{
@@ -1098,7 +1020,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
// q->setErrorString(QAbstractSocket::tr("Connection refused"));
}
emit q->stateChanged(state);
- emit q->error(socketError);
+ emit q->errorOccurred(socketError);
return;
}
@@ -1145,15 +1067,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
q, SLOT(_q_abortConnectionAttempt()),
Qt::DirectConnection);
}
- int connectTimeout = QNetworkConfigurationPrivate::DefaultTimeout;
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(q->property("_q_networksession"));
- if (networkSession) {
- QNetworkConfiguration networkConfiguration = networkSession->configuration();
- connectTimeout = networkConfiguration.connectTimeout();
- }
-#endif
- connectTimer->start(connectTimeout);
+ connectTimer->start(DefaultConnectTimeout);
}
// Wait for a write notification that will eventually call
@@ -1220,7 +1134,7 @@ void QAbstractSocketPrivate::_q_abortConnectionAttempt()
setError(QAbstractSocket::SocketTimeoutError,
QAbstractSocket::tr("Connection timed out"));
emit q->stateChanged(state);
- emit q->error(socketError);
+ emit q->errorOccurred(socketError);
} else {
_q_connectToNextAddress();
}
@@ -1367,20 +1281,23 @@ void QAbstractSocketPrivate::fetchConnectionParameters()
emit q->connected();
}
-/*! \internal
+/*! \reimp
*/
-qint64 QAbstractSocketPrivate::skip(qint64 maxSize)
+qint64 QAbstractSocket::skipData(qint64 maxSize)
{
+ Q_D(const QAbstractSocket);
+
// if we're not connected, return -1 indicating EOF
- if (!socketEngine || !socketEngine->isValid() || state != QAbstractSocket::ConnectedState)
+ if (!d->socketEngine || !d->socketEngine->isValid()
+ || d->state != QAbstractSocket::ConnectedState)
return -1;
// Caller, QIODevice::skip(), has ensured buffer is empty. So, wait
// for more data in buffered mode.
- if (isBuffered)
+ if (d->isBuffered)
return 0;
- return QIODevicePrivate::skip(maxSize);
+ return QIODevice::skipData(maxSize);
}
void QAbstractSocketPrivate::pauseSocketNotifiers(QAbstractSocket *socket)
@@ -1388,12 +1305,29 @@ void QAbstractSocketPrivate::pauseSocketNotifiers(QAbstractSocket *socket)
QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
if (!socketEngine)
return;
- socket->d_func()->prePauseReadSocketNotifierState = socketEngine->isReadNotificationEnabled();
- socket->d_func()->prePauseWriteSocketNotifierState = socketEngine->isWriteNotificationEnabled();
- socket->d_func()->prePauseExceptionSocketNotifierState = socketEngine->isExceptionNotificationEnabled();
- socketEngine->setReadNotificationEnabled(false);
- socketEngine->setWriteNotificationEnabled(false);
- socketEngine->setExceptionNotificationEnabled(false);
+ bool read = socketEngine->isReadNotificationEnabled();
+ bool write = socketEngine->isWriteNotificationEnabled();
+ bool except = socketEngine->isExceptionNotificationEnabled();
+
+#ifdef QABSTRACTSOCKET_DEBUG
+ qDebug() << socketEngine->socketDescriptor()
+ << "pause notifiers, storing 'true' states, currently read:" << read
+ << "write:" << write << "except:" << except;
+#endif
+ // We do this if-check to avoid accidentally overwriting any previously stored state
+ // It will reset to false once the socket is re-enabled.
+ if (read) {
+ socket->d_func()->prePauseReadSocketNotifierState = true;
+ socketEngine->setReadNotificationEnabled(false);
+ }
+ if (write) {
+ socket->d_func()->prePauseWriteSocketNotifierState = true;
+ socketEngine->setWriteNotificationEnabled(false);
+ }
+ if (except) {
+ socket->d_func()->prePauseExceptionSocketNotifierState = true;
+ socketEngine->setExceptionNotificationEnabled(false);
+ }
}
void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
@@ -1401,9 +1335,19 @@ void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
if (!socketEngine)
return;
- socketEngine->setReadNotificationEnabled(socket->d_func()->prePauseReadSocketNotifierState);
- socketEngine->setWriteNotificationEnabled(socket->d_func()->prePauseWriteSocketNotifierState);
- socketEngine->setExceptionNotificationEnabled(socket->d_func()->prePauseExceptionSocketNotifierState);
+ QAbstractSocketPrivate *priv = socket->d_func();
+#ifdef QABSTRACTSOCKET_DEBUG
+ qDebug() << socketEngine->socketDescriptor()
+ << "Maybe resume notifiers, read:" << priv->prePauseReadSocketNotifierState
+ << "write:" << priv->prePauseWriteSocketNotifierState
+ << "exception:" << priv->prePauseExceptionSocketNotifierState;
+#endif
+ if (std::exchange(priv->prePauseReadSocketNotifierState, false))
+ socketEngine->setReadNotificationEnabled(true);
+ if (std::exchange(priv->prePauseWriteSocketNotifierState, false))
+ socketEngine->setWriteNotificationEnabled(true);
+ if (std::exchange(priv->prePauseExceptionSocketNotifierState, false))
+ socketEngine->setExceptionNotificationEnabled(true);
}
QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket *socket)
@@ -1427,14 +1371,14 @@ void QAbstractSocketPrivate::setError(QAbstractSocket::SocketError errorCode,
\internal
Sets the socket error state to \c errorCode and \a errorString,
- and emits the QAbstractSocket::error() signal.
+ and emits the QAbstractSocket::errorOccurred() signal.
*/
void QAbstractSocketPrivate::setErrorAndEmit(QAbstractSocket::SocketError errorCode,
const QString &errorString)
{
Q_Q(QAbstractSocket);
setError(errorCode, errorString);
- emit q->error(errorCode);
+ emit q->errorOccurred(errorCode);
}
/*! \internal
@@ -1462,13 +1406,8 @@ QAbstractSocket::QAbstractSocket(SocketType socketType,
\sa socketType(), QTcpSocket, QUdpSocket
*/
QAbstractSocket::QAbstractSocket(SocketType socketType, QObject *parent)
- : QIODevice(*new QAbstractSocketPrivate, parent)
+ : QAbstractSocket(socketType, *new QAbstractSocketPrivate, parent)
{
- Q_D(QAbstractSocket);
-#if defined(QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::QAbstractSocket(%p)", parent);
-#endif
- d->socketType = socketType;
}
/*!
@@ -1616,6 +1555,19 @@ bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAb
}
/*!
+ \fn bool QAbstractSocket::bind(QHostAddress::SpecialAddress addr, quint16 port, BindMode mode)
+ \since 6.2
+ \overload
+
+ Binds to the special address \a addr on port \a port, using the BindMode \a
+ mode.
+
+ By default, the socket is bound using the DefaultForPlatform BindMode.
+ If a port is not specified, a random port is chosen.
+*/
+
+/*!
+ \fn bool QAbstractSocket::bind(quint16 port, BindMode mode)
\since 5.0
\overload
@@ -1624,10 +1576,12 @@ bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAb
By default, the socket is bound using the DefaultForPlatform BindMode.
If a port is not specified, a random port is chosen.
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QAbstractSocket::bind(quint16 port, BindMode mode)
{
return bind(QHostAddress::Any, port, mode);
}
+#endif
/*!
Returns \c true if the socket is valid and ready for use; otherwise
@@ -1656,7 +1610,7 @@ bool QAbstractSocket::isValid() const
established, QAbstractSocket enters ConnectedState and
emits connected().
- At any point, the socket can emit error() to signal that an error
+ At any point, the socket can emit errorOccurred() to signal that an error
occurred.
\a hostName may be an IP address in string form (e.g.,
@@ -1724,7 +1678,6 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
QIODevice::open(openMode);
d->readChannelCount = d->writeChannelCount = 0;
-#ifndef Q_OS_WINRT
d->state = HostLookupState;
emit stateChanged(d->state);
@@ -1762,10 +1715,6 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
(d->state == ConnectingState || d->state == HostLookupState)
? " (connection in progress)" : "");
#endif
-#else // !Q_OS_WINRT
- // On WinRT we should always connect by name. Lookup and proxy handling are done by the API.
- d->startConnectingByName(hostName);
-#endif
}
/*! \overload
@@ -1882,22 +1831,6 @@ QString QAbstractSocket::peerName() const
}
/*!
- Returns \c true if a line of data can be read from the socket;
- otherwise returns \c false.
-
- \sa readLine()
-*/
-bool QAbstractSocket::canReadLine() const
-{
- bool hasLine = QIODevice::canReadLine();
-#if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::canReadLine() == %s, buffer size = %lld, size = %lld",
- hasLine ? "true" : "false", d_func()->buffer.size(), d_func()->buffer.size());
-#endif
- return hasLine;
-}
-
-/*!
Returns the native socket descriptor of the QAbstractSocket object
if this is available; otherwise returns -1.
@@ -1941,10 +1874,6 @@ bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState
d->setError(UnsupportedSocketOperationError, tr("Operation on socket is not supported"));
return false;
}
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket engine (if it has been set)
- d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
-#endif
bool result = d->socketEngine->initialize(socketDescriptor, socketState);
if (!result) {
d->setError(d->socketEngine->error(), d->socketEngine->errorString());
@@ -1995,8 +1924,9 @@ bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState
\since 4.6
Sets the given \a option to the value described by \a value.
- \note On Windows Runtime, QAbstractSocket::KeepAliveOption must be set
- before the socket is connected.
+ \note Since the options are set on an internal socket the options
+ only apply if the socket has been created. This is only guaranteed to
+ have happened after a call to bind(), or when connected() has been emitted.
\sa socketOption()
*/
@@ -2131,12 +2061,7 @@ bool QAbstractSocket::waitForConnected(int msecs)
bool wasPendingClose = d->pendingClose;
d->pendingClose = false;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
-#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(property("_q_networksession"));
-#endif
+ QDeadlineTimer deadline{msecs};
if (d->state == HostLookupState) {
#if defined (QABSTRACTSOCKET_DEBUG)
@@ -2144,47 +2069,33 @@ bool QAbstractSocket::waitForConnected(int msecs)
#endif
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
-#ifndef QT_NO_BEARERMANAGEMENT
- if (networkSession) {
- d->_q_startConnecting(QHostInfoPrivate::fromName(d->hostName, networkSession));
- } else
-#endif
- {
- QHostAddress temp;
- if (temp.setAddress(d->hostName)) {
- QHostInfo info;
- info.setAddresses(QList<QHostAddress>() << temp);
- d->_q_startConnecting(info);
- } else {
- d->_q_startConnecting(QHostInfo::fromName(d->hostName));
- }
+ QHostAddress temp;
+ if (temp.setAddress(d->hostName)) {
+ QHostInfo info;
+ info.setAddresses(QList<QHostAddress>() << temp);
+ d->_q_startConnecting(info);
+ } else {
+ d->_q_startConnecting(QHostInfo::fromName(d->hostName));
}
}
if (state() == UnconnectedState)
return false; // connect not im progress anymore!
- int connectTimeout = QNetworkConfigurationPrivate::DefaultTimeout;
-#ifndef QT_NO_BEARERMANAGEMENT
- if (networkSession) {
- QNetworkConfiguration networkConfiguration = networkSession->configuration();
- connectTimeout = networkConfiguration.connectTimeout();
- }
-#endif
bool timedOut = true;
#if defined (QABSTRACTSOCKET_DEBUG)
int attempt = 1;
#endif
- while (state() == ConnectingState && (msecs == -1 || stopWatch.elapsed() < msecs)) {
- int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
- if (msecs != -1 && timeout > connectTimeout)
- timeout = connectTimeout;
+ while (state() == ConnectingState && !deadline.hasExpired()) {
+ QDeadlineTimer timer = deadline;
+ if (!deadline.isForever() && deadline.remainingTimeAsDuration() > DefaultConnectTimeout)
+ timer = QDeadlineTimer(DefaultConnectTimeout);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForConnected(%i) waiting %.2f secs for connection attempt #%i",
- msecs, timeout / 1000.0, attempt++);
+ msecs, timer.remainingTime() / 1000.0, attempt++);
#endif
timedOut = false;
- if (d->socketEngine && d->socketEngine->waitForWrite(timeout, &timedOut) && !timedOut) {
+ if (d->socketEngine && d->socketEngine->waitForWrite(timer, &timedOut) && !timedOut) {
d->_q_testConnection();
} else {
d->_q_connectToNextAddress();
@@ -2239,8 +2150,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
return false;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2251,11 +2161,12 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
do {
if (state() != ConnectedState && state() != BoundState)
return false;
+ Q_ASSERT(d->socketEngine);
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@@ -2273,7 +2184,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
if (readyToWrite)
d->canWriteNotification();
- } while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0);
+ } while (!deadline.hasExpired());
return false;
}
@@ -2309,8 +2220,7 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
if (d->writeBuffer.isEmpty())
return false;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2318,13 +2228,13 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
return false;
}
- forever {
+ for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite,
!d->readBufferMaxSize || d->buffer.size() < d->readBufferMaxSize,
!d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@@ -2388,8 +2298,7 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return false;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2399,12 +2308,12 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return true;
}
- forever {
+ for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, state() == ConnectedState,
!d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@@ -2440,15 +2349,6 @@ void QAbstractSocket::abort()
qDebug("QAbstractSocket::abort()");
#endif
d->setWriteChannelCount(0);
- if (d->state == UnconnectedState)
- return;
-#ifndef QT_NO_SSL
- if (QSslSocket *socket = qobject_cast<QSslSocket *>(this)) {
- socket->abort();
- return;
- }
-#endif
-
d->abortCalled = true;
close();
}
@@ -2460,23 +2360,6 @@ bool QAbstractSocket::isSequential() const
return true;
}
-/*! \reimp
-
- Returns \c true if no more data is currently
- available for reading; otherwise returns \c false.
-
- This function is most commonly used when reading data from the
- socket in a loop. For example:
-
- \snippet code/src_network_socket_qabstractsocket.cpp 2
-
- \sa bytesAvailable(), readyRead()
- */
-bool QAbstractSocket::atEnd() const
-{
- return QIODevice::atEnd();
-}
-
/*!
This function writes as much as possible from the internal write buffer to
the underlying network socket, without blocking. If any data was written,
@@ -2523,9 +2406,8 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
}
#if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]",
- data, qt_prettyDebug(data, 32, readBytes).data(), maxSize,
- readBytes);
+ qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]", data,
+ QtDebugUtils::toPrintable(data, readBytes, 32).constData(), maxSize, readBytes);
#endif
return readBytes;
}
@@ -2563,8 +2445,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
- qt_prettyDebug(data, qMin((int)size, 32), size).data(),
- size, written);
+ QtDebugUtils::toPrintable(data, size, 32).constData(), size, written);
#endif
return written; // written = actually written + what has been buffered
} else if (!d->isBuffered && d->socketType != TcpSocket) {
@@ -2575,8 +2456,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
- qt_prettyDebug(data, qMin((int)size, 32), size).data(),
- size, written);
+ QtDebugUtils::toPrintable(data, size, 32).constData(), size, written);
#endif
if (written >= 0)
d->emitBytesWritten(written);
@@ -2589,7 +2469,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
// We just write to our write buffer and enable the write notifier
// The write notifier then flush()es the buffer.
- d->writeBuffer.append(data, size);
+ d->write(data, size);
qint64 written = size;
if (d->socketEngine && !d->writeBuffer.isEmpty())
@@ -2597,8 +2477,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
- qt_prettyDebug(data, qMin((int)size, 32), size).data(),
- size, written);
+ QtDebugUtils::toPrintable(data, size, 32).constData(), size, written);
#endif
return written;
}
@@ -2929,7 +2808,7 @@ void QAbstractSocket::setSocketError(SocketError socketError)
To disable the use of a proxy for this socket, use the
QNetworkProxy::NoProxy proxy type:
- \snippet code/src_network_socket_qabstractsocket.cpp 3
+ \snippet code/src_network_socket_qabstractsocket.cpp 2
The default value for the proxy is QNetworkProxy::DefaultProxy,
which means the socket will use the application settings: if a
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index de09195eeb..1d5d45096a 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -1,46 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTSOCKET_H
#define QABSTRACTSOCKET_H
#include <QtNetwork/qtnetworkglobal.h>
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+#include <QtNetwork/qabstractsocket.h>
+#endif
+#ifdef Q_QDOC
+#include <QtNetwork/qhostaddress.h>
+#endif
#include <QtCore/qiodevice.h>
#include <QtCore/qobject.h>
#ifndef QT_NO_DEBUG_STREAM
@@ -60,6 +30,8 @@ class QAuthenticator;
class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice
{
Q_OBJECT
+ Q_MOC_INCLUDE(<QtNetwork/qauthenticator.h>)
+
public:
enum SocketType {
TcpSocket,
@@ -68,6 +40,8 @@ public:
UnknownSocketType = -1
};
Q_ENUM(SocketType)
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
enum NetworkLayerProtocol {
IPv4Protocol,
IPv6Protocol,
@@ -75,6 +49,15 @@ public:
UnknownNetworkLayerProtocol = -1
};
Q_ENUM(NetworkLayerProtocol)
+#else
+ // compatibility with Qt 4 to 6
+ using NetworkLayerProtocol = QHostAddress::NetworkLayerProtocol;
+ static constexpr auto IPv4Protocol = QHostAddress::IPv4Protocol;
+ static constexpr auto IPv6Protocol = QHostAddress::IPv6Protocol;
+ static constexpr auto AnyIPProtocol = QHostAddress::AnyIPProtocol;
+ static constexpr auto UnknownNetworkLayerProtocol = QHostAddress::UnknownNetworkLayerProtocol;
+#endif
+
enum SocketError {
ConnectionRefusedError,
RemoteHostClosedError,
@@ -144,13 +127,19 @@ public:
PauseModes pauseMode() const;
void setPauseMode(PauseModes pauseMode);
- // ### Qt6: make the first one virtual
- bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
+ virtual bool bind(const QHostAddress &address, quint16 port = 0,
+ BindMode mode = DefaultForPlatform);
+#if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || defined(Q_QDOC)
+ bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
+ { return bind(QHostAddress(addr), port, mode); }
+ bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform)
+ { return bind(QHostAddress::Any, port, mode); }
+#else
bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform);
+#endif
- // ### Qt6: de-virtualize connectToHost(QHostAddress) overload
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
- virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
+ void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
virtual void disconnectFromHost();
bool isValid() const;
@@ -158,8 +147,6 @@ public:
qint64 bytesAvailable() const override;
qint64 bytesToWrite() const override;
- bool canReadLine() const override; // ### Qt6: remove me
-
quint16 localPort() const;
QHostAddress localAddress() const;
quint16 peerPort() const;
@@ -185,7 +172,6 @@ public:
// from QIODevice
void close() override;
bool isSequential() const override;
- bool atEnd() const override; // ### Qt6: remove me
bool flush();
// for synchronous access
@@ -206,7 +192,7 @@ Q_SIGNALS:
void connected();
void disconnected();
void stateChanged(QAbstractSocket::SocketState);
- void error(QAbstractSocket::SocketError);
+ void errorOccurred(QAbstractSocket::SocketError);
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
#endif
@@ -214,6 +200,7 @@ Q_SIGNALS:
protected:
qint64 readData(char *data, qint64 maxlen) override;
qint64 readLineData(char *data, qint64 maxlen) override;
+ qint64 skipData(qint64 maxSize) override;
qint64 writeData(const char *data, qint64 len) override;
void setSocketState(SocketState state);
@@ -228,7 +215,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QAbstractSocket)
- Q_DISABLE_COPY(QAbstractSocket)
+ Q_DISABLE_COPY_MOVE(QAbstractSocket)
Q_PRIVATE_SLOT(d_func(), void _q_connectToNextAddress())
Q_PRIVATE_SLOT(d_func(), void _q_startConnecting(const QHostInfo &))
@@ -247,7 +234,9 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug, QAbstractSocket::SocketState);
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QAbstractSocket::SocketState)
-Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
+QT_DECL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketState,
+ QAbstractSocket__SocketState, Q_NETWORK_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketError,
+ QAbstractSocket__SocketError, Q_NETWORK_EXPORT)
#endif // QABSTRACTSOCKET_H
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 5aa69d747e..cc5f53179c 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTSOCKET_P_H
#define QABSTRACTSOCKET_P_H
@@ -71,9 +35,6 @@ public:
QAbstractSocketPrivate();
virtual ~QAbstractSocketPrivate();
- // from QIODevicePrivate
- qint64 skip(qint64 maxSize) override;
-
// from QAbstractSocketEngineReceiver
inline void readNotification() override { canReadNotification(); }
inline void writeNotification() override { canWriteNotification(); }
@@ -99,27 +60,27 @@ public:
void _q_testConnection();
void _q_abortConnectionAttempt();
- bool emittedReadyRead;
- bool emittedBytesWritten;
+ bool emittedReadyRead = false;
+ bool emittedBytesWritten = false;
- bool abortCalled;
- bool pendingClose;
+ bool abortCalled = false;
+ bool pendingClose = false;
- QAbstractSocket::PauseModes pauseMode;
+ QAbstractSocket::PauseModes pauseMode = QAbstractSocket::PauseNever;
QString hostName;
- quint16 port;
+ quint16 port = 0;
QHostAddress host;
QList<QHostAddress> addresses;
- quint16 localPort;
- quint16 peerPort;
+ quint16 localPort = 0;
+ quint16 peerPort = 0;
QHostAddress localAddress;
QHostAddress peerAddress;
QString peerName;
- QAbstractSocketEngine *socketEngine;
- qintptr cachedSocketDescriptor;
+ QAbstractSocketEngine *socketEngine = nullptr;
+ qintptr cachedSocketDescriptor = -1;
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy proxy;
@@ -146,25 +107,27 @@ public:
void setError(QAbstractSocket::SocketError errorCode, const QString &errorString);
void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString);
- qint64 readBufferMaxSize;
- bool isBuffered;
- bool hasPendingData;
+ qint64 readBufferMaxSize = 0;
+ bool isBuffered = false;
+ bool hasPendingData = false;
+ bool hasPendingDatagram = false;
- QTimer *connectTimer;
+ QTimer *connectTimer = nullptr;
- int hostLookupId;
+ int hostLookupId = -1;
- QAbstractSocket::SocketType socketType;
- QAbstractSocket::SocketState state;
+ QAbstractSocket::SocketType socketType = QAbstractSocket::UnknownSocketType;
+ QAbstractSocket::SocketState state = QAbstractSocket::UnconnectedState;
// Must be kept in sync with QIODevicePrivate::errorString.
- QAbstractSocket::SocketError socketError;
+ QAbstractSocket::SocketError socketError = QAbstractSocket::UnknownSocketError;
- QAbstractSocket::NetworkLayerProtocol preferredNetworkLayerProtocol;
+ QAbstractSocket::NetworkLayerProtocol preferredNetworkLayerProtocol =
+ QAbstractSocket::UnknownNetworkLayerProtocol;
- bool prePauseReadSocketNotifierState;
- bool prePauseWriteSocketNotifierState;
- bool prePauseExceptionSocketNotifierState;
+ bool prePauseReadSocketNotifierState = false;
+ bool prePauseWriteSocketNotifierState = false;
+ bool prePauseExceptionSocketNotifierState = false;
static void pauseSocketNotifiers(QAbstractSocket*);
static void resumeSocketNotifiers(QAbstractSocket*);
static QAbstractSocketEngine* getSocketEngine(QAbstractSocket*);
diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp
index 54c7452c66..864ce8d9ea 100644
--- a/src/network/socket/qabstractsocketengine.cpp
+++ b/src/network/socket/qabstractsocketengine.cpp
@@ -1,49 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qabstractsocketengine_p.h"
-#ifndef Q_OS_WINRT
#include "qnativesocketengine_p.h"
-#else
-#include "qnativesocketengine_winrt_p.h"
-#endif
#include "qmutex.h"
#include "qnetworkproxy.h"
@@ -77,7 +37,7 @@ QSocketEngineHandler::~QSocketEngineHandler()
QAbstractSocketEnginePrivate::QAbstractSocketEnginePrivate()
: socketError(QAbstractSocket::UnknownSocketError)
, hasSetSocketError(false)
- , socketErrorString(QLatin1String(QT_TRANSLATE_NOOP(QSocketLayer, "Unknown error")))
+ , socketErrorString(QLatin1StringView(QT_TRANSLATE_NOOP(QSocketLayer, "Unknown error")))
, socketState(QAbstractSocket::UnconnectedState)
, socketType(QAbstractSocket::UnknownSocketType)
, socketProtocol(QAbstractSocket::UnknownNetworkLayerProtocol)
@@ -274,3 +234,5 @@ int QAbstractSocketEngine::outboundStreamCount() const
}
QT_END_NAMESPACE
+
+#include "moc_qabstractsocketengine_p.cpp"
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 112e7032d6..9ee79cebc1 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTSOCKETENGINE_P_H
#define QABSTRACTSOCKETENGINE_P_H
@@ -55,8 +19,9 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qabstractsocket.h"
-#include "private/qobject_p.h"
+#include <QtCore/qdeadlinetimer.h>
#include "private/qnetworkdatagram_p.h"
+#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -80,9 +45,12 @@ public:
#endif
};
+static constexpr std::chrono::seconds DefaultTimeout{30};
+
class Q_AUTOTEST_EXPORT QAbstractSocketEngine : public QObject
{
Q_OBJECT
+ Q_MOC_INCLUDE(<QtNetwork/qauthenticator.h>)
public:
static QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent);
@@ -132,8 +100,8 @@ public:
virtual bool connectToHost(const QHostAddress &address, quint16 port) = 0;
virtual bool connectToHostByName(const QString &name, quint16 port) = 0;
virtual bool bind(const QHostAddress &address, quint16 port) = 0;
- virtual bool listen() = 0;
- virtual int accept() = 0;
+ virtual bool listen(int backlog) = 0;
+ virtual qintptr accept() = 0;
virtual void close() = 0;
virtual qint64 bytesAvailable() const = 0;
@@ -163,11 +131,14 @@ public:
virtual int option(SocketOption option) const = 0;
virtual bool setOption(SocketOption option, int value) = 0;
- virtual bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) = 0;
- virtual bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) = 0;
+ virtual bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
+ virtual bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) = 0;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
QAbstractSocket::SocketError error() const;
QString errorString() const;
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index 76b3053224..725bc21359 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpsocketengine_p.h"
#include "qtcpsocket.h"
@@ -43,7 +7,7 @@
#include "qurl.h"
#include "private/qhttpnetworkreply_p.h"
#include "private/qiodevice_p.h"
-#include "qelapsedtimer.h"
+#include "qdeadlinetimer.h"
#include "qnetworkinterface.h"
#if !defined(QT_NO_NETWORKPROXY)
@@ -51,6 +15,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#define DEBUG
QHttpSocketEngine::QHttpSocketEngine(QObject *parent)
@@ -72,9 +38,6 @@ bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSo
setSocketType(type);
d->socket = new QTcpSocket(this);
d->reply = new QHttpNetworkReply(QUrl(), this);
-#ifndef QT_NO_BEARERMANAGEMENT
- d->socket->setProperty("_q_networkSession", property("_q_networkSession"));
-#endif
// Explicitly disable proxying on the proxy socket itself to avoid
// unwanted recursion.
@@ -93,7 +56,7 @@ bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSo
connect(d->socket, SIGNAL(bytesWritten(qint64)),
this, SLOT(slotSocketBytesWritten()),
Qt::DirectConnection);
- connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ connect(d->socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
Qt::DirectConnection);
connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
@@ -189,24 +152,22 @@ bool QHttpSocketEngine::connectToHostByName(const QString &hostname, quint16 por
bool QHttpSocketEngine::bind(const QHostAddress &, quint16)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return false;
}
-bool QHttpSocketEngine::listen()
+bool QHttpSocketEngine::listen(int backlog)
{
+ Q_UNUSED(backlog);
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return false;
}
-int QHttpSocketEngine::accept()
+qintptr QHttpSocketEngine::accept()
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return -1;
}
@@ -241,8 +202,7 @@ qint64 QHttpSocketEngine::read(char *data, qint64 maxlen)
// failed, return the socket's error. Otherwise, fall through and
// return as much as we read so far.
close();
- setError(QAbstractSocket::RemoteHostClosedError,
- QLatin1String("Remote host closed"));
+ setError(QAbstractSocket::RemoteHostClosedError, "Remote host closed"_L1);
setState(QAbstractSocket::UnconnectedState);
return -1;
}
@@ -261,8 +221,7 @@ bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &,
const QNetworkInterface &)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return false;
}
@@ -270,8 +229,7 @@ bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &,
const QNetworkInterface &)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return false;
}
@@ -283,8 +241,7 @@ QNetworkInterface QHttpSocketEngine::multicastInterface() const
bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return false;
}
#endif // QT_NO_NETWORKINTERFACE
@@ -305,16 +262,14 @@ qint64 QHttpSocketEngine::pendingDatagramSize() const
qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return -1;
}
qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &)
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
+ setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
return -1;
}
@@ -355,19 +310,16 @@ bool QHttpSocketEngine::setOption(SocketOption option, int value)
return false;
}
-bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QHttpSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
return false;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
// Wait for more data if nothing is available.
if (!d->socket->bytesAvailable()) {
- if (!d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->socket->waitForReadyRead(deadline.remainingTime())) {
if (d->socket->state() == QAbstractSocket::UnconnectedState)
return true;
setError(d->socket->error(), d->socket->errorString());
@@ -377,11 +329,7 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
}
}
- // If we're not connected yet, wait until we are, or until an error
- // occurs.
- while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
- // Loop while the protocol handshake is taking place.
- }
+ waitForProtocolHandshake(deadline);
// Report any error that may occur.
if (d->state != Connected) {
@@ -393,14 +341,14 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
return true;
}
-bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QHttpSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
// If we're connected, just forward the call.
if (d->state == Connected) {
if (d->socket->bytesToWrite()) {
- if (!d->socket->waitForBytesWritten(msecs)) {
+ if (!d->socket->waitForBytesWritten(deadline.remainingTime())) {
if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
*timedOut = true;
return false;
@@ -409,15 +357,7 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
return true;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
-
- // If we're not connected yet, wait until we are, and until bytes have
- // been received (i.e., the socket has connected, we have sent the
- // greeting, and then received the response).
- while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
- // Loop while the protocol handshake is taking place.
- }
+ waitForProtocolHandshake(deadline);
// Report any error that may occur.
if (d->state != Connected) {
@@ -431,25 +371,37 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
// Not interested in writing? Then we wait for read notifications.
- bool canRead = waitForRead(msecs, timedOut);
+ bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
// Interested in writing? Then we wait for write notifications.
- bool canWrite = waitForWrite(msecs, timedOut);
+ bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;
}
+void QHttpSocketEngine::waitForProtocolHandshake(QDeadlineTimer deadline) const
+{
+ Q_D(const QHttpSocketEngine);
+
+ // If we're not connected yet, wait until we are (and until bytes have
+ // been received, i.e. the socket has connected, we have sent the
+ // greeting, and then received the response), or until an error occurs.
+ while (d->state != Connected && d->socket->waitForReadyRead(deadline.remainingTime())) {
+ // Loop while the protocol handshake is taking place.
+ }
+}
+
bool QHttpSocketEngine::isReadNotificationEnabled() const
{
Q_D(const QHttpSocketEngine);
@@ -515,11 +467,14 @@ void QHttpSocketEngine::slotSocketConnected()
data += " HTTP/1.1\r\n";
data += "Proxy-Connection: keep-alive\r\n";
data += "Host: " + peerAddress + "\r\n";
- if (!d->proxy.hasRawHeader("User-Agent"))
+ const auto headers = d->proxy.headers();
+ if (!headers.contains(QHttpHeaders::WellKnownHeader::UserAgent))
data += "User-Agent: Mozilla/5.0\r\n";
- const auto headers = d->proxy.rawHeaderList();
- for (const QByteArray &header : headers)
- data += header + ": " + d->proxy.rawHeader(header) + "\r\n";
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ const auto name = headers.nameAt(i);
+ data += QByteArrayView(name.data(), name.size()) + ": "
+ + headers.valueAt(i) + "\r\n";
+ }
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
if (priv && priv->method != QAuthenticatorPrivate::None) {
@@ -581,7 +536,7 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->pendingResponseData -= uint(skipped);
if (d->pendingResponseData > 0)
return;
- if (d->reply->d_func()->statusCode == 407)
+ if (d->reply->statusCode() == 407)
d->state = SendAuthentication;
}
@@ -601,16 +556,8 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->authenticator.detach();
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
- if (d->credentialsSent && priv->phase != QAuthenticatorPrivate::Phase2) {
- // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is not currently in progress.
- //407 response again means the provided username/password were invalid.
- d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted.
- d->authenticator.detach();
- priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
- priv->hasFailed = true;
- }
-
- priv->parseHttpResponse(d->reply->header(), true);
+ const auto headers = d->reply->header();
+ priv->parseHttpResponse(headers, true, d->proxy.hostName());
if (priv->phase == QAuthenticatorPrivate::Invalid) {
// problem parsing the reply
@@ -621,6 +568,29 @@ void QHttpSocketEngine::slotSocketReadNotification()
return;
}
+ if (priv->phase == QAuthenticatorPrivate::Done
+ || (priv->phase == QAuthenticatorPrivate::Start
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate))) {
+ if (priv->phase == QAuthenticatorPrivate::Start)
+ priv->phase = QAuthenticatorPrivate::Phase1;
+ bool credentialsWasSent = d->credentialsSent;
+ if (d->credentialsSent) {
+ // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is
+ // not currently in progress. 407 response again means the provided
+ // username/password were invalid.
+ d->authenticator.detach();
+ priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ priv->hasFailed = true;
+ d->credentialsSent = false;
+ priv->phase = QAuthenticatorPrivate::Done;
+ }
+ if ((priv->method != QAuthenticatorPrivate::Ntlm
+ && priv->method != QAuthenticatorPrivate::Negotiate)
+ || credentialsWasSent)
+ proxyAuthenticationRequired(d->proxy, &d->authenticator);
+ }
+
bool willClose;
QByteArray proxyConnectionHeader = d->reply->headerField("Proxy-Connection");
// Although most proxies use the unofficial Proxy-Connection header, the Connection header
@@ -645,13 +615,11 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->socket->readAll();
//We're done with the reply and need to reset it for the next connection
delete d->reply;
- d->reply = new QHttpNetworkReply;
+ d->reply = new QHttpNetworkReply(QUrl(), this);
}
- if (priv->phase == QAuthenticatorPrivate::Done)
- proxyAuthenticationRequired(d->proxy, &d->authenticator);
- // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
if (priv->phase == QAuthenticatorPrivate::Done) {
+ d->authenticator = QAuthenticator();
setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
d->socket->disconnectFromHost();
} else {
@@ -793,7 +761,7 @@ void QHttpSocketEngine::emitReadNotification()
{
Q_D(QHttpSocketEngine);
// if there is a connection notification pending we have to emit the readNotification
- // incase there is connection error. This is only needed for Windows, but it does not
+ // in case there is connection error. This is only needed for Windows, but it does not
// hurt in other cases.
if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
d->readNotificationPending = true;
@@ -866,3 +834,5 @@ QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(qintptr, QOb
QT_END_NAMESPACE
#endif // !QT_NO_NETWORKPROXY
+
+#include "moc_qhttpsocketengine_p.cpp"
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index 0c2c450c81..7926abf513 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPSOCKETENGINE_P_H
#define QHTTPSOCKETENGINE_P_H
@@ -52,10 +16,12 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "private/qabstractsocketengine_p.h"
+
+#include <QtNetwork/qnetworkproxy.h>
+
#include "qabstractsocket.h"
-#include "qnetworkproxy.h"
#include "private/qauthenticator_p.h"
+#include "private/qabstractsocketengine_p.h"
QT_REQUIRE_CONFIG(http);
@@ -95,8 +61,8 @@ public:
bool connectToHost(const QHostAddress &address, quint16 port) override;
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
- bool listen() override;
- int accept() override;
+ bool listen(int backlog) override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -126,11 +92,16 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+
+ void waitForProtocolHandshake(QDeadlineTimer deadline) const;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index 5ca2db70b9..5ef2db6b94 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalserver.h"
#include "qlocalserver_p.h"
@@ -47,6 +11,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QLocalServer
\since 4.4
@@ -90,6 +56,8 @@ QT_BEGIN_NAMESPACE
socket. This changes the access permissions on platforms (Linux, Windows)
that support access permissions on the socket. Both GroupAccess and OtherAccess
may vary slightly in meanings depending on the platform.
+ On Linux and Android it is possible to use sockets with abstract addresses;
+ socket permissions have no meaning for such sockets.
\value NoOptions No access restrictions have been set.
\value UserAccessOption
@@ -102,6 +70,10 @@ QT_BEGIN_NAMESPACE
Access is available to everyone on Windows.
\value WorldAccessOption
No access restrictions.
+ \value AbstractNamespaceOption
+ The listening socket will be created in the abstract namespace. This flag is specific to Linux.
+ In case of other platforms, for the sake of code portability, this flag is equivalent
+ to WorldAccessOption.
\sa socketOptions
*/
@@ -138,8 +110,9 @@ QLocalServer::~QLocalServer()
\property QLocalServer::socketOptions
\since 5.0
- The setSocketOptions method controls how the socket operates.
- For example the socket may restrict access to what user ids can
+ \brief the socket options that control how the socket operates.
+
+ For example, the socket may restrict access to what user ids can
connect to the socket.
These options must be set before listen() is called.
@@ -147,7 +120,7 @@ QLocalServer::~QLocalServer()
In some cases, such as with Unix domain sockets on Linux, the
access to the socket will be determined by file system permissions,
and are created based on the umask. Setting the access flags will
- overide this and will restrict or permit access as specified.
+ override this and will restrict or permit access as specified.
Other Unix-based operating systems, such as \macos, do not
honor file permissions for Unix domain sockets and by default
@@ -160,6 +133,11 @@ QLocalServer::~QLocalServer()
in the Windows documentation). OtherAccessOption refers to
the well known "Everyone" group.
+ On Linux platforms it is possible to create a socket in the abstract
+ namespace, which is independent of the filesystem. Using this kind
+ of socket implies ignoring permission options. On other platforms
+ AbstractNamespaceOption is equivalent to WorldAccessOption.
+
By default none of the flags are set, access permissions
are the platform default.
@@ -184,6 +162,12 @@ QLocalServer::SocketOptions QLocalServer::socketOptions() const
return d->socketOptions;
}
+QBindable<QLocalServer::SocketOptions> QLocalServer::bindableSocketOptions()
+{
+ Q_D(QLocalServer);
+ return &d->socketOptions;
+}
+
/*!
\since 5.10
Returns the native socket descriptor the server uses to listen
@@ -192,10 +176,9 @@ QLocalServer::SocketOptions QLocalServer::socketOptions() const
The type of the descriptor depends on the platform:
\list
\li On Windows, the returned value is a
- \l{https://msdn.microsoft.com/en-us/library/windows/desktop/ms740522(v=vs.85).aspx}
- {Winsock 2 Socket Handle}.
+ \l{Winsock 2 Socket Handle}.
- \li With WinRT and on INTEGRITY, the returned value is the
+ \li On INTEGRITY, the returned value is the
QTcpServer socket descriptor and the type is defined by
\l{QTcpServer::socketDescriptor}{socketDescriptor}.
@@ -282,9 +265,27 @@ bool QLocalServer::hasPendingConnections() const
*/
void QLocalServer::incomingConnection(quintptr socketDescriptor)
{
- Q_D(QLocalServer);
QLocalSocket *socket = new QLocalSocket(this);
socket->setSocketDescriptor(socketDescriptor);
+ addPendingConnection(socket);
+}
+
+/*!
+ This function is called by QLocalServer::incomingConnection()
+ to add the \a socket to the list of pending incoming connections.
+
+ \note Don't forget to call this member from reimplemented
+ incomingConnection() if you do not want to break the
+ Pending Connections mechanism. This function emits the
+ pendingConnectionAvailable() signal after the socket has been
+ added.
+
+ \sa incomingConnection(), pendingConnectionAvailable()
+ \since 6.8
+*/
+void QLocalServer::addPendingConnection(QLocalSocket *socket)
+{
+ Q_D(QLocalServer);
d->pendingConnections.enqueue(socket);
emit newConnection();
}
@@ -331,7 +332,7 @@ bool QLocalServer::listen(const QString &name)
if (name.isEmpty()) {
d->error = QAbstractSocket::HostNotFoundError;
- QString function = QLatin1String("QLocalServer::listen");
+ QString function = "QLocalServer::listen"_L1;
d->errorString = tr("%1: Name error").arg(function);
return false;
}
@@ -358,7 +359,9 @@ bool QLocalServer::listen(const QString &name)
serverName(), fullServerName() may return a string with
a name if this option is supported by the platform;
- otherwise, they return an empty QString.
+ otherwise, they return an empty QString. In particular, the addresses
+ of sockets in the abstract namespace supported by Linux will
+ not yield useful names if they contain unprintable characters.
\sa isListening(), close()
*/
@@ -534,6 +537,36 @@ bool QLocalServer::waitForNewConnection(int msec, bool *timedOut)
return !d->pendingConnections.isEmpty();
}
+/*!
+ Sets the backlog queue size of to be accepted connections to \a
+ size. The operating system might reduce or ignore this value.
+ By default, the queue size is 50.
+
+ \note This property must be set prior to calling listen().
+
+ \since 6.3
+
+ \sa listenBacklogSize()
+*/
+void QLocalServer::setListenBacklogSize(int size)
+{
+ Q_D(QLocalServer);
+ d->listenBacklog = size;
+}
+
+/*!
+ Returns the backlog queue size of to be accepted connections.
+
+ \since 6.3
+
+ \sa setListenBacklogSize()
+*/
+int QLocalServer::listenBacklogSize() const
+{
+ Q_D(const QLocalServer);
+ return d->listenBacklog;
+}
+
QT_END_NAMESPACE
#include "moc_qlocalserver.cpp"
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
index 211aa94d85..685253e8be 100644
--- a/src/network/socket/qlocalserver.h
+++ b/src/network/socket/qlocalserver.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLOCALSERVER_H
#define QLOCALSERVER_H
@@ -43,6 +7,8 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtCore/qproperty.h>
+
QT_REQUIRE_CONFIG(localserver);
QT_BEGIN_NAMESPACE
@@ -54,7 +20,8 @@ class Q_NETWORK_EXPORT QLocalServer : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QLocalServer)
- Q_PROPERTY(SocketOptions socketOptions READ socketOptions WRITE setSocketOptions)
+ Q_PROPERTY(SocketOptions socketOptions READ socketOptions WRITE setSocketOptions
+ BINDABLE bindableSocketOptions)
Q_SIGNALS:
void newConnection();
@@ -65,9 +32,10 @@ public:
UserAccessOption = 0x01,
GroupAccessOption = 0x2,
OtherAccessOption = 0x4,
- WorldAccessOption = 0x7
+ WorldAccessOption = 0x7,
+ AbstractNamespaceOption = 0x8
};
- Q_FLAG(SocketOption)
+ Q_ENUM(SocketOption)
Q_DECLARE_FLAGS(SocketOptions, SocketOption)
Q_FLAG(SocketOptions)
@@ -89,13 +57,18 @@ public:
void setMaxPendingConnections(int numConnections);
bool waitForNewConnection(int msec = 0, bool *timedOut = nullptr);
+ void setListenBacklogSize(int size);
+ int listenBacklogSize() const;
+
void setSocketOptions(SocketOptions options);
SocketOptions socketOptions() const;
+ QBindable<SocketOptions> bindableSocketOptions();
qintptr socketDescriptor() const;
protected:
virtual void incomingConnection(quintptr socketDescriptor);
+ void addPendingConnection(QLocalSocket *socket);
private:
Q_DISABLE_COPY(QLocalServer)
diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h
index 92616e59ce..f7afd5cd32 100644
--- a/src/network/socket/qlocalserver_p.h
+++ b/src/network/socket/qlocalserver_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLOCALSERVER_P_H
#define QLOCALSERVER_P_H
@@ -61,6 +25,7 @@ QT_REQUIRE_CONFIG(localserver);
#if defined(QT_LOCALSOCKET_TCP)
# include <qtcpserver.h>
+# include <QtCore/qmap.h>
#elif defined(Q_OS_WIN)
# include <qt_windows.h>
# include <qwineventnotifier.h>
@@ -99,15 +64,18 @@ public:
QMap<quintptr, QTcpSocket*> socketMap;
#elif defined(Q_OS_WIN)
struct Listener {
- HANDLE handle;
+ Listener() = default;
+ HANDLE handle = nullptr;
OVERLAPPED overlapped;
- bool connected;
+ bool connected = false;
+ private:
+ Q_DISABLE_COPY(Listener)
};
void setError(const QString &function);
bool addListener();
- QList<Listener> listeners;
+ std::vector<std::unique_ptr<Listener>> listeners;
HANDLE eventHandle;
QWinEventNotifier *connectionEventNotifier;
#else
@@ -123,7 +91,9 @@ public:
QQueue<QLocalSocket*> pendingConnections;
QString errorString;
QAbstractSocket::SocketError error;
- QLocalServer::SocketOptions socketOptions;
+ int listenBacklog = 50;
+
+ Q_OBJECT_BINDABLE_PROPERTY(QLocalServerPrivate, QLocalServer::SocketOptions, socketOptions)
};
QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalserver_tcp.cpp b/src/network/socket/qlocalserver_tcp.cpp
index b3f1188afa..4d52d2c23a 100644
--- a/src/network/socket/qlocalserver_tcp.cpp
+++ b/src/network/socket/qlocalserver_tcp.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalserver.h"
#include "qlocalserver_p.h"
@@ -48,6 +12,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
void QLocalServerPrivate::init()
{
Q_Q(QLocalServer);
@@ -56,16 +22,18 @@ void QLocalServerPrivate::init()
bool QLocalServerPrivate::listen(const QString &requestedServerName)
{
+ tcpServer.setListenBacklogSize(listenBacklog);
+
if (!tcpServer.listen(QHostAddress::LocalHost))
return false;
- const QLatin1String prefix("QLocalServer/");
+ const auto prefix = "QLocalServer/"_L1;
if (requestedServerName.startsWith(prefix))
fullServerName = requestedServerName;
else
fullServerName = prefix + requestedServerName;
- QSettings settings(QLatin1String("QtProject"), QLatin1String("Qt"));
+ QSettings settings("QtProject"_L1, "Qt"_L1);
if (settings.contains(fullServerName)) {
qWarning("QLocalServer::listen: server name is already in use.");
tcpServer.close();
@@ -83,8 +51,8 @@ bool QLocalServerPrivate::listen(qintptr socketDescriptor)
void QLocalServerPrivate::closeServer()
{
- QSettings settings(QLatin1String("QtProject"), QLatin1String("Qt"));
- if (fullServerName == QLatin1String("QLocalServer"))
+ QSettings settings("QtProject"_L1, "Qt"_L1);
+ if (fullServerName == "QLocalServer"_L1)
settings.setValue(fullServerName, QVariant());
else
settings.remove(fullServerName);
@@ -115,14 +83,14 @@ void QLocalServerPrivate::_q_onNewConnection()
bool QLocalServerPrivate::removeServer(const QString &name)
{
- const QLatin1String prefix("QLocalServer/");
+ const auto prefix = "QLocalServer/"_L1;
QString serverName;
if (name.startsWith(prefix))
serverName = name;
else
serverName = prefix + name;
- QSettings settings(QLatin1String("QtProject"), QLatin1String("Qt"));
+ QSettings settings("QtProject"_L1, "Qt"_L1);
if (settings.contains(serverName))
settings.remove(serverName);
diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp
index 88367d680d..9aa9a5b86f 100644
--- a/src/network/socket/qlocalserver_unix.cpp
+++ b/src/network/socket/qlocalserver_unix.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalserver.h"
#include "qlocalserver_p.h"
@@ -44,6 +8,7 @@
#include "qnet_unix_p.h"
#include "qtemporarydir.h"
+#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -51,12 +16,33 @@
#include <qdir.h>
#include <qdatetime.h>
+#include <optional>
+
#ifdef Q_OS_VXWORKS
# include <selectLib.h>
#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+QLocalServer::SocketOptions optionsForPlatform(QLocalServer::SocketOptions srcOptions)
+{
+ // For OS that does not support abstract namespace the AbstractNamespaceOption
+ // means that we go for WorldAccessOption - as it is the closest option in
+ // regards of access rights. In Linux/Android case we clean-up the access rights.
+
+ if (srcOptions.testFlag(QLocalServer::AbstractNamespaceOption)) {
+ if (PlatformSupportsAbstractNamespace)
+ return QLocalServer::AbstractNamespaceOption;
+ else
+ return QLocalServer::WorldAccessOption;
+ }
+ return srcOptions;
+}
+}
+
void QLocalServerPrivate::init()
{
}
@@ -64,11 +50,11 @@ void QLocalServerPrivate::init()
bool QLocalServerPrivate::removeServer(const QString &name)
{
QString fileName;
- if (name.startsWith(QLatin1Char('/'))) {
+ if (name.startsWith(u'/')) {
fileName = name;
} else {
fileName = QDir::cleanPath(QDir::tempPath());
- fileName += QLatin1Char('/') + name;
+ fileName += u'/' + name;
}
if (QFile::exists(fileName))
return QFile::remove(fileName);
@@ -80,50 +66,66 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
{
Q_Q(QLocalServer);
+ // socket options adjusted for current platform
+ auto options = optionsForPlatform(socketOptions.value());
+
// determine the full server path
- if (requestedServerName.startsWith(QLatin1Char('/'))) {
+ if (options.testFlag(QLocalServer::AbstractNamespaceOption)
+ || requestedServerName.startsWith(u'/')) {
fullServerName = requestedServerName;
} else {
fullServerName = QDir::cleanPath(QDir::tempPath());
- fullServerName += QLatin1Char('/') + requestedServerName;
+ fullServerName += u'/' + requestedServerName;
}
serverName = requestedServerName;
QByteArray encodedTempPath;
const QByteArray encodedFullServerName = QFile::encodeName(fullServerName);
- QScopedPointer<QTemporaryDir> tempDir;
+ std::optional<QTemporaryDir> tempDir;
- // Check any of the flags
- if (socketOptions & QLocalServer::WorldAccessOption) {
+ if (options & QLocalServer::WorldAccessOption) {
QFileInfo serverNameFileInfo(fullServerName);
- tempDir.reset(new QTemporaryDir(serverNameFileInfo.absolutePath() + QLatin1Char('/')));
+ tempDir.emplace(serverNameFileInfo.absolutePath() + u'/');
if (!tempDir->isValid()) {
- setError(QLatin1String("QLocalServer::listen"));
+ setError("QLocalServer::listen"_L1);
return false;
}
- encodedTempPath = QFile::encodeName(tempDir->path() + QLatin1String("/s"));
+ encodedTempPath = QFile::encodeName(tempDir->path() + "/s"_L1);
}
// create the unix socket
listenSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0);
if (-1 == listenSocket) {
- setError(QLatin1String("QLocalServer::listen"));
+ setError("QLocalServer::listen"_L1);
closeServer();
return false;
}
// Construct the unix address
struct ::sockaddr_un addr;
+
addr.sun_family = PF_UNIX;
- if (sizeof(addr.sun_path) < (uint)encodedFullServerName.size() + 1) {
- setError(QLatin1String("QLocalServer::listen"));
+ ::memset(addr.sun_path, 0, sizeof(addr.sun_path));
+
+ // for abstract namespace add 2 to length, to take into account trailing AND leading null
+ constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
+
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedFullServerName.size() + extraCharacters)) {
+ setError("QLocalServer::listen"_L1);
closeServer();
return false;
}
- if (socketOptions & QLocalServer::WorldAccessOption) {
- if (sizeof(addr.sun_path) < (uint)encodedTempPath.size() + 1) {
- setError(QLatin1String("QLocalServer::listen"));
+ QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
+ if (options.testFlag(QLocalServer::AbstractNamespaceOption)) {
+ // Abstract socket address is distinguished by the fact
+ // that sun_path[0] is a null byte ('\0')
+ ::memcpy(addr.sun_path + 1, encodedFullServerName.constData(),
+ encodedFullServerName.size() + 1);
+ addrSize = offsetof(::sockaddr_un, sun_path) + encodedFullServerName.size() + 1;
+ } else if (options & QLocalServer::WorldAccessOption) {
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedTempPath.size() + 1)) {
+ setError("QLocalServer::listen"_L1);
closeServer();
return false;
}
@@ -135,10 +137,10 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
}
// bind
- if(-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) {
- setError(QLatin1String("QLocalServer::listen"));
+ if (-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, addrSize)) {
+ setError("QLocalServer::listen"_L1);
// if address is in use already, just close the socket, but do not delete the file
- if(errno == EADDRINUSE)
+ if (errno == EADDRINUSE)
QT_CLOSE(listenSocket);
// otherwise, close the socket and delete the file
else
@@ -148,35 +150,32 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
}
// listen for connections
- if (-1 == qt_safe_listen(listenSocket, 50)) {
- setError(QLatin1String("QLocalServer::listen"));
+ if (-1 == qt_safe_listen(listenSocket, listenBacklog)) {
+ setError("QLocalServer::listen"_L1);
closeServer();
- listenSocket = -1;
- if (error != QAbstractSocket::AddressInUseError)
- QFile::remove(fullServerName);
return false;
}
- if (socketOptions & QLocalServer::WorldAccessOption) {
+ if (options & QLocalServer::WorldAccessOption) {
mode_t mode = 000;
- if (socketOptions & QLocalServer::UserAccessOption)
+ if (options & QLocalServer::UserAccessOption)
mode |= S_IRWXU;
- if (socketOptions & QLocalServer::GroupAccessOption)
+ if (options & QLocalServer::GroupAccessOption)
mode |= S_IRWXG;
- if (socketOptions & QLocalServer::OtherAccessOption)
+ if (options & QLocalServer::OtherAccessOption)
mode |= S_IRWXO;
if (::chmod(encodedTempPath.constData(), mode) == -1) {
- setError(QLatin1String("QLocalServer::listen"));
+ setError("QLocalServer::listen"_L1);
closeServer();
return false;
}
if (::rename(encodedTempPath.constData(), encodedFullServerName.constData()) == -1) {
- setError(QLatin1String("QLocalServer::listen"));
+ setError("QLocalServer::listen"_L1);
closeServer();
return false;
}
@@ -185,7 +184,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName)
Q_ASSERT(!socketNotifier);
socketNotifier = new QSocketNotifier(listenSocket,
QSocketNotifier::Read, q);
- q->connect(socketNotifier, SIGNAL(activated(int)),
+ q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
q, SLOT(_q_onNewConnection()));
socketNotifier->setEnabled(maxPendingConnections > 0);
return true;
@@ -201,33 +200,26 @@ bool QLocalServerPrivate::listen(qintptr socketDescriptor)
::fcntl(listenSocket, F_SETFD, FD_CLOEXEC);
::fcntl(listenSocket, F_SETFL, ::fcntl(listenSocket, F_GETFL) | O_NONBLOCK);
-#ifdef Q_OS_LINUX
+ bool abstractAddress = false;
struct ::sockaddr_un addr;
QT_SOCKLEN_T len = sizeof(addr);
memset(&addr, 0, sizeof(addr));
- if (0 == ::getsockname(listenSocket, (sockaddr *)&addr, &len)) {
- // check for absract sockets
- if (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0) {
- addr.sun_path[0] = '@';
- }
- QString name = QString::fromLatin1(addr.sun_path);
- if (!name.isEmpty()) {
- fullServerName = name;
- serverName = fullServerName.mid(fullServerName.lastIndexOf(QLatin1Char('/')) + 1);
- if (serverName.isEmpty()) {
- serverName = fullServerName;
- }
+ if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) == 0) {
+#if defined(Q_OS_QNX)
+ if (addr.sun_path[0] == 0 && addr.sun_path[1] == 0)
+ len = SUN_LEN(&addr);
+#endif
+ if (QLocalSocketPrivate::parseSockaddr(addr, len, fullServerName, serverName,
+ abstractAddress)) {
+ QLocalServer::SocketOptions options = socketOptions.value();
+ socketOptions = options.setFlag(QLocalServer::AbstractNamespaceOption, abstractAddress);
}
}
-#else
- serverName.clear();
- fullServerName.clear();
-#endif
Q_ASSERT(!socketNotifier);
socketNotifier = new QSocketNotifier(listenSocket,
QSocketNotifier::Read, q);
- q->connect(socketNotifier, SIGNAL(activated(int)),
+ q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
q, SLOT(_q_onNewConnection()));
socketNotifier->setEnabled(maxPendingConnections > 0);
return true;
@@ -250,8 +242,13 @@ void QLocalServerPrivate::closeServer()
QT_CLOSE(listenSocket);
listenSocket = -1;
- if (!fullServerName.isEmpty())
+ if (!fullServerName.isEmpty()
+ && !optionsForPlatform(socketOptions).testFlag(QLocalServer::AbstractNamespaceOption)) {
QFile::remove(fullServerName);
+ }
+
+ serverName.clear();
+ fullServerName.clear();
}
/*!
@@ -269,8 +266,8 @@ void QLocalServerPrivate::_q_onNewConnection()
::sockaddr_un addr;
QT_SOCKLEN_T length = sizeof(sockaddr_un);
int connectedSocket = qt_safe_accept(listenSocket, (sockaddr *)&addr, &length);
- if(-1 == connectedSocket) {
- setError(QLatin1String("QLocalSocket::activated"));
+ if (-1 == connectedSocket) {
+ setError("QLocalSocket::activated"_L1);
closeServer();
} else {
socketNotifier->setEnabled(pendingConnections.size()
@@ -282,8 +279,7 @@ void QLocalServerPrivate::_q_onNewConnection()
void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
{
pollfd pfd = qt_make_pollfd(listenSocket, POLLIN);
-
- switch (qt_poll_msecs(&pfd, 1, msec)) {
+ switch (qt_safe_poll(&pfd, 1, QDeadlineTimer(msec))) {
case 0:
if (timedOut)
*timedOut = true;
@@ -299,7 +295,7 @@ void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
errno = EBADF;
Q_FALLTHROUGH();
case -1:
- setError(QLatin1String("QLocalServer::waitForNewConnection"));
+ setError("QLocalServer::waitForNewConnection"_L1);
closeServer();
break;
}
diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp
index 2d71a7e730..bc761b1e8f 100644
--- a/src/network/socket/qlocalserver_win.cpp
+++ b/src/network/socket/qlocalserver_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalserver.h"
#include "qlocalserver_p.h"
@@ -53,17 +17,16 @@
// before it is read. Pipewriter is used for write buffering.
#define BUFSIZE 0
-// ###: This should be a property. Should replace the insane 50 on unix as well.
-#define SYSTEM_MAX_PENDING_SOCKETS 8
-
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
bool QLocalServerPrivate::addListener()
{
// The object must not change its address once the
// contained OVERLAPPED struct is passed to Windows.
- listeners << Listener();
- Listener &listener = listeners.last();
+ listeners.push_back(std::make_unique<Listener>());
+ auto &listener = listeners.back();
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
@@ -77,10 +40,10 @@ bool QLocalServerPrivate::addListener()
QByteArray tokenGroupBuffer;
// create security descriptor if access options were specified
- if ((socketOptions & QLocalServer::WorldAccessOption)) {
+ if ((socketOptions.value() & QLocalServer::WorldAccessOption)) {
pSD.reset(new SECURITY_DESCRIPTOR);
if (!InitializeSecurityDescriptor(pSD.data(), SECURITY_DESCRIPTOR_REVISION)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
return false;
}
HANDLE hToken = NULL;
@@ -91,7 +54,7 @@ bool QLocalServerPrivate::addListener()
tokenUserBuffer.fill(0, dwBufferSize);
auto pTokenUser = reinterpret_cast<PTOKEN_USER>(tokenUserBuffer.data());
if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwBufferSize)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
CloseHandle(hToken);
return false;
}
@@ -101,7 +64,7 @@ bool QLocalServerPrivate::addListener()
tokenGroupBuffer.fill(0, dwBufferSize);
auto pTokenGroup = reinterpret_cast<PTOKEN_PRIMARY_GROUP>(tokenGroupBuffer.data());
if (!GetTokenInformation(hToken, TokenPrimaryGroup, pTokenGroup, dwBufferSize, &dwBufferSize)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
CloseHandle(hToken);
return false;
}
@@ -128,7 +91,7 @@ bool QLocalServerPrivate::addListener()
if (!AllocateAndInitializeSid(&WorldAuth, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&worldSID)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
return false;
}
@@ -143,23 +106,23 @@ bool QLocalServerPrivate::addListener()
auto acl = reinterpret_cast<PACL>(aclBuffer.data());
InitializeAcl(acl, aclSize, ACL_REVISION_DS);
- if (socketOptions & QLocalServer::UserAccessOption) {
+ if (socketOptions.value() & QLocalServer::UserAccessOption) {
if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenUser->User.Sid)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
FreeSid(worldSID);
return false;
}
}
- if (socketOptions & QLocalServer::GroupAccessOption) {
+ if (socketOptions.value() & QLocalServer::GroupAccessOption) {
if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenGroup->PrimaryGroup)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
FreeSid(worldSID);
return false;
}
}
- if (socketOptions & QLocalServer::OtherAccessOption) {
+ if (socketOptions.value() & QLocalServer::OtherAccessOption) {
if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, worldSID)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
FreeSid(worldSID);
return false;
}
@@ -167,7 +130,7 @@ bool QLocalServerPrivate::addListener()
SetSecurityDescriptorOwner(pSD.data(), pTokenUser->User.Sid, FALSE);
SetSecurityDescriptorGroup(pSD.data(), pTokenGroup->PrimaryGroup, FALSE);
if (!SetSecurityDescriptorDacl(pSD.data(), TRUE, acl, FALSE)) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
+ setError("QLocalServerPrivate::addListener"_L1);
FreeSid(worldSID);
return false;
}
@@ -175,7 +138,7 @@ bool QLocalServerPrivate::addListener()
sa.lpSecurityDescriptor = pSD.data();
}
- listener.handle = CreateNamedPipe(
+ listener->handle = CreateNamedPipe(
reinterpret_cast<const wchar_t *>(fullServerName.utf16()), // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // byte type pipe
@@ -187,32 +150,32 @@ bool QLocalServerPrivate::addListener()
3000, // client time-out
&sa);
- if (listener.handle == INVALID_HANDLE_VALUE) {
- setError(QLatin1String("QLocalServerPrivate::addListener"));
- listeners.removeLast();
+ if (listener->handle == INVALID_HANDLE_VALUE) {
+ setError("QLocalServerPrivate::addListener"_L1);
+ listeners.pop_back();
return false;
}
if (worldSID)
FreeSid(worldSID);
- memset(&listener.overlapped, 0, sizeof(listener.overlapped));
- listener.overlapped.hEvent = eventHandle;
+ memset(&listener->overlapped, 0, sizeof(OVERLAPPED));
+ listener->overlapped.hEvent = eventHandle;
// Beware! ConnectNamedPipe will reset the eventHandle to non-signaled.
// Callers of addListener must check all listeners for connections.
- if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) {
+ if (!ConnectNamedPipe(listener->handle, &listener->overlapped)) {
switch (GetLastError()) {
case ERROR_IO_PENDING:
- listener.connected = false;
+ listener->connected = false;
break;
case ERROR_PIPE_CONNECTED:
- listener.connected = true;
+ listener->connected = true;
break;
default:
- CloseHandle(listener.handle);
- setError(QLatin1String("QLocalServerPrivate::addListener"));
- listeners.removeLast();
+ CloseHandle(listener->handle);
+ setError("QLocalServerPrivate::addListener"_L1);
+ listeners.pop_back();
return false;
}
} else {
@@ -243,7 +206,7 @@ bool QLocalServerPrivate::listen(const QString &name)
{
Q_Q(QLocalServer);
- const QLatin1String pipePath("\\\\.\\pipe\\");
+ const auto pipePath = "\\\\.\\pipe\\"_L1;
if (name.startsWith(pipePath))
fullServerName = name;
else
@@ -256,7 +219,7 @@ bool QLocalServerPrivate::listen(const QString &name)
connectionEventNotifier = new QWinEventNotifier(eventHandle , q);
q->connect(connectionEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onNewConnection()));
- for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i)
+ for (int i = 0; i < listenBacklog; ++i)
if (!addListener())
return false;
@@ -284,12 +247,12 @@ void QLocalServerPrivate::_q_onNewConnection()
// Testing shows that there is indeed absolutely no guarantee which listener gets
// a client connection first, so there is no way around polling all of them.
- for (int i = 0; i < listeners.size(); ) {
- HANDLE handle = listeners[i].handle;
- if (listeners[i].connected
- || GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE))
+ for (size_t i = 0; i < listeners.size(); ) {
+ HANDLE handle = listeners[i]->handle;
+ if (listeners[i]->connected
+ || GetOverlappedResult(handle, &listeners[i]->overlapped, &dummy, FALSE))
{
- listeners.removeAt(i);
+ listeners.erase(listeners.begin() + i);
addListener();
@@ -303,7 +266,7 @@ void QLocalServerPrivate::_q_onNewConnection()
} else {
if (GetLastError() != ERROR_IO_INCOMPLETE) {
q->close();
- setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection"));
+ setError("QLocalServerPrivate::_q_onNewConnection"_L1);
return;
}
@@ -319,8 +282,8 @@ void QLocalServerPrivate::closeServer()
connectionEventNotifier->deleteLater();
connectionEventNotifier = 0;
CloseHandle(eventHandle);
- for (int i = 0; i < listeners.size(); ++i)
- CloseHandle(listeners[i].handle);
+ for (size_t i = 0; i < listeners.size(); ++i)
+ CloseHandle(listeners[i]->handle);
listeners.clear();
}
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
index d35f838af5..dff7e42849 100644
--- a/src/network/socket/qlocalsocket.cpp
+++ b/src/network/socket/qlocalsocket.cpp
@@ -1,41 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qlocalsocket.h"
#include "qlocalsocket_p.h"
@@ -51,7 +17,7 @@ QT_BEGIN_NAMESPACE
On Windows this is a named pipe and on Unix this is a local domain socket.
- If an error occurs, socketError() returns the type of error, and
+ If an error occurs, error() returns the type of error, and
errorString() can be called to get a human readable description
of what happened.
@@ -64,6 +30,21 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \enum QLocalSocket::SocketOption
+ \since 6.2
+ This enum describes the possible options that can be used to connect to
+ a server. Currently, on Linux and Android it is used for specifying
+ connection to a server listening to a socket bound to an abstract address.
+
+ \value NoOptions No options have been set.
+ \value AbstractNamespaceOption
+ The socket will try to connect to an abstract address. This flag is specific
+ to Linux and Android. On other platforms is ignored.
+
+ \sa socketOptions
+*/
+
+/*!
\fn void QLocalSocket::connectToServer(OpenMode openMode)
\since 5.1
@@ -74,7 +55,7 @@ QT_BEGIN_NAMESPACE
The socket is opened in the given \a openMode and first enters ConnectingState.
If a connection is established, QLocalSocket enters ConnectedState and emits connected().
- After calling this function, the socket can emit error() to signal that an error occurred.
+ After calling this function, the socket can emit errorOccurred() to signal that an error occurred.
\sa state(), serverName(), waitForConnected()
*/
@@ -87,8 +68,8 @@ QT_BEGIN_NAMESPACE
Note that unlike in most other QIODevice subclasses, open() may not open the device directly.
The function return false if the socket was already connected or if the server to connect
- to was not defined and true in any other case. The connected() or error() signals will be
- emitted once the device is actualy open (or the connection failed).
+ to was not defined and true in any other case. The connected() or errorOccurred() signals will be
+ emitted once the device is actually open (or the connection failed).
See connectToServer() for more details.
*/
@@ -130,10 +111,9 @@ QT_BEGIN_NAMESPACE
\list
\li On Windows, the returned value is a
- \l{https://msdn.microsoft.com/en-us/library/windows/desktop/ms740522(v=vs.85).aspx}
- {Winsock 2 Socket Handle}.
+ \l{Winsock 2 Socket Handle}.
- \li With WinRT and on INTEGRITY, the returned value is the
+ \li On INTEGRITY, the returned value is the
QTcpSocket socket descriptor and the type is defined by
\l{QTcpSocket::socketDescriptor}{socketDescriptor}.
@@ -150,6 +130,16 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
+ \reimp
+*/
+
+/*!
+ \fn qint64 QLocalSocket::skipData(qint64 maxSize)
+ \reimp
+*/
+
+/*!
\fn qint64 QLocalSocket::writeData(const char *data, qint64 c)
\reimp
*/
@@ -181,7 +171,14 @@ QT_BEGIN_NAMESPACE
/*!
\fn void QLocalSocket::close()
- \reimp
+
+ Closes the I/O device for the socket and calls disconnectFromServer()
+ to close the socket's connection.
+
+ See QIODevice::close() for a description of the actions that occur when an I/O
+ device is closed.
+
+ \sa abort()
*/
/*!
@@ -212,7 +209,7 @@ QT_BEGIN_NAMESPACE
Attempts to close the socket. If there is pending data waiting to be
written, QLocalSocket will enter ClosingState and wait until all data
has been written. Eventually, it will enter UnconnectedState and emit
- the disconnectedFromServer() signal.
+ the disconnected() signal.
\sa connectToServer()
*/
@@ -328,7 +325,8 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QLocalSocket::error(QLocalSocket::LocalSocketError socketError)
+ \fn void QLocalSocket::errorOccurred(QLocalSocket::LocalSocketError socketError)
+ \since 5.15
This signal is emitted after an error occurred. The \a socketError
parameter describes the type of error that occurred.
@@ -361,6 +359,8 @@ QLocalSocket::QLocalSocket(QObject * parent)
: QIODevice(*new QLocalSocketPrivate, parent)
{
Q_D(QLocalSocket);
+
+ d->readBufferChunkSize = 0; // force QIODevice unbuffered mode
d->init();
}
@@ -369,7 +369,7 @@ QLocalSocket::QLocalSocket(QObject * parent)
*/
QLocalSocket::~QLocalSocket()
{
- QLocalSocket::close();
+ abort();
#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP)
Q_D(QLocalSocket);
d->unixSocket.setParent(nullptr);
@@ -389,7 +389,7 @@ bool QLocalSocket::open(OpenMode openMode)
The socket is opened in the given \a openMode and first enters ConnectingState.
If a connection is established, QLocalSocket enters ConnectedState and emits connected().
- After calling this function, the socket can emit error() to signal that an error occurred.
+ After calling this function, the socket can emit errorOccurred() to signal that an error occurred.
\sa state(), serverName(), waitForConnected()
*/
@@ -431,6 +431,37 @@ QString QLocalSocket::serverName() const
}
/*!
+ \property QLocalSocket::socketOptions
+ \since 6.2
+ \brief the socket options.
+
+ Options must be set while the socket is in \l{UnconnectedState} state.
+
+ \sa connectToServer()
+ */
+QLocalSocket::SocketOptions QLocalSocket::socketOptions() const
+{
+ Q_D(const QLocalSocket);
+ return d->socketOptions;
+}
+
+void QLocalSocket::setSocketOptions(QLocalSocket::SocketOptions option)
+{
+ Q_D(QLocalSocket);
+ if (d->state != UnconnectedState) {
+ qWarning("QLocalSocket::setSocketOptions() called while not in unconnected state");
+ return;
+ }
+ d->socketOptions = option;
+}
+
+QBindable<QLocalSocket::SocketOptions> QLocalSocket::bindableSocketOptions()
+{
+ Q_D(QLocalSocket);
+ return &d->socketOptions;
+}
+
+/*!
Returns the server path that the socket is connected to.
\note The return value of this function is platform specific.
@@ -539,6 +570,9 @@ QDebug operator<<(QDebug debug, QLocalSocket::LocalSocketError error)
case QLocalSocket::UnknownSocketError:
debug << "QLocalSocket::UnknownSocketError";
break;
+ case QLocalSocket::OperationError:
+ debug << "QLocalSocket::OperationError";
+ break;
default:
debug << "QLocalSocket::SocketError(" << int(error) << ')';
break;
diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h
index 1876a6ac0d..2bd9689a05 100644
--- a/src/network/socket/qlocalsocket.h
+++ b/src/network/socket/qlocalsocket.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLOCALSOCKET_H
#define QLOCALSOCKET_H
@@ -44,6 +8,10 @@
#include <QtCore/qiodevice.h>
#include <QtNetwork/qabstractsocket.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
QT_REQUIRE_CONFIG(localserver);
QT_BEGIN_NAMESPACE
@@ -54,6 +22,8 @@ class Q_NETWORK_EXPORT QLocalSocket : public QIODevice
{
Q_OBJECT
Q_DECLARE_PRIVATE(QLocalSocket)
+ Q_PROPERTY(SocketOptions socketOptions READ socketOptions WRITE setSocketOptions
+ BINDABLE bindableSocketOptions)
public:
enum LocalSocketError
@@ -79,6 +49,13 @@ public:
ClosingState = QAbstractSocket::ClosingState
};
+ enum SocketOption {
+ NoOptions = 0x00,
+ AbstractNamespaceOption = 0x01
+ };
+ Q_DECLARE_FLAGS(SocketOptions, SocketOption)
+ Q_FLAG(SocketOptions)
+
QLocalSocket(QObject *parent = nullptr);
~QLocalSocket();
@@ -108,6 +85,10 @@ public:
OpenMode openMode = ReadWrite);
qintptr socketDescriptor() const;
+ void setSocketOptions(SocketOptions option);
+ SocketOptions socketOptions() const;
+ QBindable<SocketOptions> bindableSocketOptions();
+
LocalSocketState state() const;
bool waitForBytesWritten(int msecs = 30000) override;
bool waitForConnected(int msecs = 30000);
@@ -117,36 +98,38 @@ public:
Q_SIGNALS:
void connected();
void disconnected();
- void error(QLocalSocket::LocalSocketError socketError);
+ void errorOccurred(QLocalSocket::LocalSocketError socketError);
void stateChanged(QLocalSocket::LocalSocketState socketState);
protected:
virtual qint64 readData(char*, qint64) override;
+ qint64 readLineData(char *data, qint64 maxSize) override;
+ qint64 skipData(qint64 maxSize) override;
virtual qint64 writeData(const char*, qint64) override;
private:
Q_DISABLE_COPY(QLocalSocket)
#if defined(QT_LOCALSOCKET_TCP)
Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAbstractSocket::SocketState))
- Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_errorOccurred(QAbstractSocket::SocketError))
#elif defined(Q_OS_WIN)
- Q_PRIVATE_SLOT(d_func(), void _q_canWrite())
Q_PRIVATE_SLOT(d_func(), void _q_pipeClosed())
Q_PRIVATE_SLOT(d_func(), void _q_winError(ulong, const QString &))
#else
Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAbstractSocket::SocketState))
- Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_errorOccurred(QAbstractSocket::SocketError))
Q_PRIVATE_SLOT(d_func(), void _q_connectToSocket())
Q_PRIVATE_SLOT(d_func(), void _q_abortConnectionAttempt())
#endif
};
#ifndef QT_NO_DEBUG_STREAM
-#include <QtCore/qdebug.h>
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketError);
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketState);
#endif
+Q_DECLARE_OPERATORS_FOR_FLAGS(QLocalSocket::SocketOptions)
+
QT_END_NAMESPACE
#endif // QLOCALSOCKET_H
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index d93b53be0c..82446161c7 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLOCALSOCKET_P_H
#define QLOCALSOCKET_P_H
@@ -73,9 +37,12 @@ QT_REQUIRE_CONFIG(localserver);
# include <errno.h>
#endif
+struct sockaddr_un;
+
QT_BEGIN_NAMESPACE
#if !defined(Q_OS_WIN) || defined(QT_LOCALSOCKET_TCP)
+
class QLocalUnixSocket : public QTcpSocket
{
@@ -113,51 +80,59 @@ public:
class QLocalSocketPrivate : public QIODevicePrivate
{
+public:
Q_DECLARE_PUBLIC(QLocalSocket)
-public:
QLocalSocketPrivate();
void init();
#if defined(QT_LOCALSOCKET_TCP)
- qint64 skip(qint64 maxSize) override;
QLocalUnixSocket* tcpSocket;
bool ownsTcpSocket;
void setSocket(QLocalUnixSocket*);
QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const;
- void errorOccurred(QLocalSocket::LocalSocketError, const QString &function);
+ void setErrorAndEmit(QLocalSocket::LocalSocketError, const QString &function);
void _q_stateChanged(QAbstractSocket::SocketState newState);
- void _q_error(QAbstractSocket::SocketError newError);
+ void _q_errorOccurred(QAbstractSocket::SocketError newError);
#elif defined(Q_OS_WIN)
~QLocalSocketPrivate();
- void destroyPipeHandles();
- void _q_canWrite();
+ qint64 pipeWriterBytesToWrite() const;
+ void _q_canRead();
+ void _q_bytesWritten(qint64 bytes);
void _q_pipeClosed();
void _q_winError(ulong windowsError, const QString &function);
+ void _q_writeFailed();
HANDLE handle;
QWindowsPipeWriter *pipeWriter;
QWindowsPipeReader *pipeReader;
QLocalSocket::LocalSocketError error;
#else
- qint64 skip(qint64 maxSize) override;
QLocalUnixSocket unixSocket;
QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const;
- void errorOccurred(QLocalSocket::LocalSocketError, const QString &function);
+ void setErrorAndEmit(QLocalSocket::LocalSocketError, const QString &function);
void _q_stateChanged(QAbstractSocket::SocketState newState);
- void _q_error(QAbstractSocket::SocketError newError);
+ void _q_errorOccurred(QAbstractSocket::SocketError newError);
void _q_connectToSocket();
void _q_abortConnectionAttempt();
void cancelDelayedConnect();
+ void describeSocket(qintptr socketDescriptor);
+ static bool parseSockaddr(const sockaddr_un &addr, uint len,
+ QString &fullServerName, QString &serverName, bool &abstractNamespace);
QSocketNotifier *delayConnect;
QTimer *connectTimer;
- int connectingSocket;
QString connectingName;
+ int connectingSocket;
QIODevice::OpenMode connectingOpenMode;
#endif
-
+ QLocalSocket::LocalSocketState state;
QString serverName;
QString fullServerName;
- QLocalSocket::LocalSocketState state;
+#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP)
+ bool emittedReadyRead;
+ bool emittedBytesWritten;
+#endif
+
+ Q_OBJECT_BINDABLE_PROPERTY(QLocalSocketPrivate, QLocalSocket::SocketOptions, socketOptions)
};
QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalsocket_tcp.cpp b/src/network/socket/qlocalsocket_tcp.cpp
index 41e5b47627..c7870b3dff 100644
--- a/src/network/socket/qlocalsocket_tcp.cpp
+++ b/src/network/socket/qlocalsocket_tcp.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalsocket.h"
#include "qlocalsocket_p.h"
@@ -47,6 +11,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
tcpSocket(0),
ownsTcpSocket(true),
@@ -68,7 +34,6 @@ void QLocalSocketPrivate::setSocket(QLocalUnixSocket* socket)
Q_Q(QLocalSocket);
// QIODevice signals
- q->connect(tcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
q->connect(tcpSocket, SIGNAL(bytesWritten(qint64)),
q, SIGNAL(bytesWritten(qint64)));
q->connect(tcpSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
@@ -77,25 +42,20 @@ void QLocalSocketPrivate::setSocket(QLocalUnixSocket* socket)
q->connect(tcpSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
q->connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
- q->connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
- q, SLOT(_q_error(QAbstractSocket::SocketError)));
+ q->connect(tcpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
+ q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
q->connect(tcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
tcpSocket->setParent(q);
}
-qint64 QLocalSocketPrivate::skip(qint64 maxSize)
-{
- return tcpSocket->skip(maxSize);
-}
-
-void QLocalSocketPrivate::_q_error(QAbstractSocket::SocketError socketError)
+void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
{
Q_Q(QLocalSocket);
- QString function = QLatin1String("QLocalSocket");
+ QString function = "QLocalSocket"_L1;
QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
QString errorString = generateErrorString(error, function);
q->setErrorString(errorString);
- emit q->error(error);
+ emit q->errorOccurred(error);
}
void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
@@ -168,7 +128,7 @@ QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError
return errorString;
}
-void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
+void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
{
Q_Q(QLocalSocket);
switch (error) {
@@ -206,7 +166,7 @@ void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, co
QString errorString = generateErrorString(error, function);
q->setErrorString(errorString);
- emit q->error(error);
+ emit q->errorOccurred(error);
// errors cause a disconnect
tcpSocket->setSocketState(QAbstractSocket::UnconnectedState);
@@ -222,7 +182,7 @@ void QLocalSocket::connectToServer(OpenMode openMode)
Q_D(QLocalSocket);
if (state() == ConnectedState || state() == ConnectingState) {
setErrorString(tr("Trying to connect while connection is in progress"));
- emit error(QLocalSocket::OperationError);
+ emit errorOccurred(QLocalSocket::OperationError);
return;
}
@@ -231,23 +191,21 @@ void QLocalSocket::connectToServer(OpenMode openMode)
emit stateChanged(d->state);
if (d->serverName.isEmpty()) {
- d->errorOccurred(ServerNotFoundError,
- QLatin1String("QLocalSocket::connectToServer"));
+ d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
return;
}
- const QLatin1String prefix("QLocalServer/");
+ const auto prefix = "QLocalServer/"_L1;
if (d->serverName.startsWith(prefix))
d->fullServerName = d->serverName;
else
d->fullServerName = prefix + d->serverName;
- QSettings settings(QLatin1String("QtProject"), QLatin1String("Qt"));
+ QSettings settings("QtProject"_L1, "Qt"_L1);
bool ok;
const quint16 port = settings.value(d->fullServerName).toUInt(&ok);
if (!ok) {
- d->errorOccurred(ServerNotFoundError,
- QLatin1String("QLocalSocket::connectToServer"));
+ d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
return;
}
QIODevice::open(openMode);
@@ -306,6 +264,21 @@ qint64 QLocalSocket::readData(char *data, qint64 c)
return d->tcpSocket->read(data, c);
}
+qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
+{
+ if (!maxSize)
+ return 0;
+
+ // QIODevice::readLine() reserves space for the trailing '\0' byte,
+ // so we must read 'maxSize + 1' bytes.
+ return d_func()->tcpSocket->readLine(data, maxSize + 1);
+}
+
+qint64 QLocalSocket::skipData(qint64 maxSize)
+{
+ return d_func()->tcpSocket->skip(maxSize);
+}
+
qint64 QLocalSocket::writeData(const char *data, qint64 c)
{
Q_D(QLocalSocket);
@@ -316,6 +289,7 @@ void QLocalSocket::abort()
{
Q_D(QLocalSocket);
d->tcpSocket->abort();
+ close();
}
qint64 QLocalSocket::bytesAvailable() const
@@ -339,10 +313,11 @@ bool QLocalSocket::canReadLine() const
void QLocalSocket::close()
{
Q_D(QLocalSocket);
+
+ QIODevice::close();
d->tcpSocket->close();
d->serverName.clear();
d->fullServerName.clear();
- QIODevice::close();
}
bool QLocalSocket::waitForBytesWritten(int msecs)
diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp
index 7de9a7d4c7..af0dc988af 100644
--- a/src/network/socket/qlocalsocket_unix.cpp
+++ b/src/network/socket/qlocalsocket_unix.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalsocket.h"
#include "qlocalsocket_p.h"
@@ -49,22 +13,51 @@
#include <errno.h>
#include <qdir.h>
+#include <qdeadlinetimer.h>
#include <qdebug.h>
-#include <qelapsedtimer.h>
+#include <qstringconverter.h>
#ifdef Q_OS_VXWORKS
# include <selectLib.h>
#endif
+using namespace std::chrono_literals;
+
#define QT_CONNECT_TIMEOUT 30000
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+// determine the full server path
+static QString pathNameForConnection(const QString &connectingName,
+ QLocalSocket::SocketOptions options)
+{
+ if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
+ || connectingName.startsWith(u'/')) {
+ return connectingName;
+ }
+
+ return QDir::tempPath() + u'/' + connectingName;
+}
+
+static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
+{
+ // For OS that does not support abstract namespace the AbstractNamespaceOption
+ // option is cleared.
+ if (!PlatformSupportsAbstractNamespace)
+ return QLocalSocket::NoOptions;
+ return srcOptions;
+}
+}
+
QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
delayConnect(nullptr),
connectTimer(nullptr),
connectingSocket(-1),
- state(QLocalSocket::UnconnectedState)
+ state(QLocalSocket::UnconnectedState),
+ socketOptions(QLocalSocket::NoOptions)
{
}
@@ -72,7 +65,6 @@ void QLocalSocketPrivate::init()
{
Q_Q(QLocalSocket);
// QIODevice signals
- q->connect(&unixSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
q, SIGNAL(bytesWritten(qint64)));
q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
@@ -81,25 +73,20 @@ void QLocalSocketPrivate::init()
q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
- q->connect(&unixSocket, SIGNAL(error(QAbstractSocket::SocketError)),
- q, SLOT(_q_error(QAbstractSocket::SocketError)));
+ q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
+ q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
unixSocket.setParent(q);
}
-qint64 QLocalSocketPrivate::skip(qint64 maxSize)
-{
- return unixSocket.skip(maxSize);
-}
-
-void QLocalSocketPrivate::_q_error(QAbstractSocket::SocketError socketError)
+void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
{
Q_Q(QLocalSocket);
- QString function = QLatin1String("QLocalSocket");
+ QString function = "QLocalSocket"_L1;
QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
QString errorString = generateErrorString(error, function);
q->setErrorString(errorString);
- emit q->error(error);
+ emit q->errorOccurred(error);
}
void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
@@ -172,7 +159,7 @@ QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError
return errorString;
}
-void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
+void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
{
Q_Q(QLocalSocket);
switch (error) {
@@ -210,7 +197,7 @@ void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, co
QString errorString = generateErrorString(error, function);
q->setErrorString(errorString);
- emit q->error(error);
+ emit q->errorOccurred(error);
// errors cause a disconnect
unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
@@ -225,9 +212,9 @@ void QLocalSocket::connectToServer(OpenMode openMode)
{
Q_D(QLocalSocket);
if (state() == ConnectedState || state() == ConnectingState) {
- QString errorString = d->generateErrorString(QLocalSocket::OperationError, QLatin1String("QLocalSocket::connectToserver"));
+ QString errorString = d->generateErrorString(QLocalSocket::OperationError, "QLocalSocket::connectToserver"_L1);
setErrorString(errorString);
- emit error(QLocalSocket::OperationError);
+ emit errorOccurred(QLocalSocket::OperationError);
return;
}
@@ -237,15 +224,13 @@ void QLocalSocket::connectToServer(OpenMode openMode)
emit stateChanged(d->state);
if (d->serverName.isEmpty()) {
- d->errorOccurred(ServerNotFoundError,
- QLatin1String("QLocalSocket::connectToServer"));
+ d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
return;
}
// create the socket
if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
- d->errorOccurred(UnsupportedSocketOperationError,
- QLatin1String("QLocalSocket::connectToServer"));
+ d->setErrorAndEmit(UnsupportedSocketOperationError, "QLocalSocket::connectToServer"_L1);
return;
}
@@ -266,49 +251,55 @@ void QLocalSocket::connectToServer(OpenMode openMode)
void QLocalSocketPrivate::_q_connectToSocket()
{
Q_Q(QLocalSocket);
- QString connectingPathName;
-
- // determine the full server path
- if (connectingName.startsWith(QLatin1Char('/'))) {
- connectingPathName = connectingName;
- } else {
- connectingPathName = QDir::tempPath();
- connectingPathName += QLatin1Char('/') + connectingName;
- }
+ QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
+ const QString connectingPathName = pathNameForConnection(connectingName, options);
const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
- struct sockaddr_un name;
- name.sun_family = PF_UNIX;
- if (sizeof(name.sun_path) < (uint)encodedConnectingPathName.size() + 1) {
- QString function = QLatin1String("QLocalSocket::connectToServer");
- errorOccurred(QLocalSocket::ServerNotFoundError, function);
+ struct ::sockaddr_un addr;
+ addr.sun_family = PF_UNIX;
+ memset(addr.sun_path, 0, sizeof(addr.sun_path));
+
+ // for abstract socket add 2 to length, to take into account trailing AND leading null
+ constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
+
+ if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
+ QString function = "QLocalSocket::connectToServer"_L1;
+ setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
return;
}
- ::memcpy(name.sun_path, encodedConnectingPathName.constData(),
- encodedConnectingPathName.size() + 1);
- if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
- QString function = QLatin1String("QLocalSocket::connectToServer");
+
+ QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
+ if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
+ ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
+ encodedConnectingPathName.size() + 1);
+ addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
+ } else {
+ ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
+ encodedConnectingPathName.size() + 1);
+ }
+ if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&addr, addrSize)) {
+ QString function = "QLocalSocket::connectToServer"_L1;
switch (errno)
{
case EINVAL:
case ECONNREFUSED:
- errorOccurred(QLocalSocket::ConnectionRefusedError, function);
+ setErrorAndEmit(QLocalSocket::ConnectionRefusedError, function);
break;
case ENOENT:
- errorOccurred(QLocalSocket::ServerNotFoundError, function);
+ setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
break;
case EACCES:
case EPERM:
- errorOccurred(QLocalSocket::SocketAccessError, function);
+ setErrorAndEmit(QLocalSocket::SocketAccessError, function);
break;
case ETIMEDOUT:
- errorOccurred(QLocalSocket::SocketTimeoutError, function);
+ setErrorAndEmit(QLocalSocket::SocketTimeoutError, function);
break;
case EAGAIN:
// Try again later, all of the sockets listening are full
if (!delayConnect) {
delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
- q->connect(delayConnect, SIGNAL(activated(int)), q, SLOT(_q_connectToSocket()));
+ q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
}
if (!connectTimer) {
connectTimer = new QTimer(q);
@@ -320,7 +311,7 @@ void QLocalSocketPrivate::_q_connectToSocket()
delayConnect->setEnabled(true);
break;
default:
- errorOccurred(QLocalSocket::UnknownSocketError, function);
+ setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
}
return;
}
@@ -332,11 +323,11 @@ void QLocalSocketPrivate::_q_connectToSocket()
fullServerName = connectingPathName;
if (unixSocket.setSocketDescriptor(connectingSocket,
QAbstractSocket::ConnectedState, connectingOpenMode)) {
- q->QIODevice::open(connectingOpenMode | QIODevice::Unbuffered);
+ q->QIODevice::open(connectingOpenMode);
q->emit connected();
} else {
- QString function = QLatin1String("QLocalSocket::connectToServer");
- errorOccurred(QLocalSocket::UnknownSocketError, function);
+ QString function = "QLocalSocket::connectToServer"_L1;
+ setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
}
connectingSocket = -1;
connectingName.clear();
@@ -364,10 +355,69 @@ bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
}
QIODevice::open(openMode);
d->state = socketState;
+ d->describeSocket(socketDescriptor);
return d->unixSocket.setSocketDescriptor(socketDescriptor,
newSocketState, openMode);
}
+void QLocalSocketPrivate::describeSocket(qintptr socketDescriptor)
+{
+ bool abstractAddress = false;
+
+ struct ::sockaddr_un addr;
+ QT_SOCKLEN_T len = sizeof(addr);
+ memset(&addr, 0, sizeof(addr));
+ const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
+ if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
+ // this is the case when we call it from QLocalServer, then there is no peername
+ len = sizeof(addr);
+ if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
+ return;
+ }
+ if (parseSockaddr(addr, static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
+ QLocalSocket::SocketOptions options = socketOptions.value();
+ socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
+ }
+}
+
+bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
+ uint len,
+ QString &fullServerName,
+ QString &serverName,
+ bool &abstractNamespace)
+{
+ if (len <= offsetof(::sockaddr_un, sun_path))
+ return false;
+ len -= offsetof(::sockaddr_un, sun_path);
+ // check for abstract socket address
+ abstractNamespace = PlatformSupportsAbstractNamespace
+ && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
+ QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
+ // An abstract socket address can be arbitrary binary. To properly handle such a case,
+ // we'd have to add new access functions for this very specific case. Instead, we just
+ // attempt to decode it according to OS text encoding. If it fails we ignore the result.
+ QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
+ len - (abstractNamespace ? 1 : 0));
+ QString name = toUtf16(textData);
+ if (!name.isEmpty() && !toUtf16.hasError()) {
+ //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
+ //chop them off as \0 character is not allowed in filenames
+ if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1('\0'))) {
+ int truncPos = name.size() - 1;
+ while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
+ truncPos--;
+ name.truncate(truncPos);
+ }
+ fullServerName = name;
+ serverName = abstractNamespace
+ ? name
+ : fullServerName.mid(fullServerName.lastIndexOf(u'/') + 1);
+ if (serverName.isEmpty())
+ serverName = fullServerName;
+ }
+ return true;
+}
+
void QLocalSocketPrivate::_q_abortConnectionAttempt()
{
Q_Q(QLocalSocket);
@@ -398,6 +448,21 @@ qint64 QLocalSocket::readData(char *data, qint64 c)
return d->unixSocket.read(data, c);
}
+qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
+{
+ if (!maxSize)
+ return 0;
+
+ // QIODevice::readLine() reserves space for the trailing '\0' byte,
+ // so we must read 'maxSize + 1' bytes.
+ return d_func()->unixSocket.readLine(data, maxSize + 1);
+}
+
+qint64 QLocalSocket::skipData(qint64 maxSize)
+{
+ return d_func()->unixSocket.skip(maxSize);
+}
+
qint64 QLocalSocket::writeData(const char *data, qint64 c)
{
Q_D(QLocalSocket);
@@ -408,6 +473,7 @@ void QLocalSocket::abort()
{
Q_D(QLocalSocket);
d->unixSocket.abort();
+ close();
}
qint64 QLocalSocket::bytesAvailable() const
@@ -431,6 +497,8 @@ bool QLocalSocket::canReadLine() const
void QLocalSocket::close()
{
Q_D(QLocalSocket);
+
+ QIODevice::close();
d->unixSocket.close();
d->cancelDelayedConnect();
if (d->connectingSocket != -1)
@@ -440,7 +508,6 @@ void QLocalSocket::close()
d->connectingOpenMode = { };
d->serverName.clear();
d->fullServerName.clear();
- QIODevice::close();
}
bool QLocalSocket::waitForBytesWritten(int msecs)
@@ -519,21 +586,20 @@ bool QLocalSocket::waitForConnected(int msec)
if (state() != ConnectingState)
return (state() == ConnectedState);
- QElapsedTimer timer;
- timer.start();
-
pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
- do {
- const int timeout = (msec > 0) ? qMax(msec - timer.elapsed(), Q_INT64_C(0)) : msec;
- const int result = qt_poll_msecs(&pfd, 1, timeout);
+ QDeadlineTimer deadline{msec};
+ auto remainingTime = deadline.remainingTimeAsDuration();
+ do {
+ const int result = qt_safe_poll(&pfd, 1, deadline);
if (result == -1)
- d->errorOccurred(QLocalSocket::UnknownSocketError,
- QLatin1String("QLocalSocket::waitForConnected"));
+ d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
+ "QLocalSocket::waitForConnected"_L1);
else if (result > 0)
d->_q_connectToSocket();
- } while (state() == ConnectingState && !timer.hasExpired(msec));
+ } while (state() == ConnectingState
+ && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
return (state() == ConnectedState);
}
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index 4decbd5ded..b3f3f9002a 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -1,51 +1,86 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlocalsocket_p.h"
+#include <qscopedvaluerollback.h>
+#include <qdeadlinetimer.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+struct QSocketPoller
+{
+ QSocketPoller(const QLocalSocketPrivate &socket);
+
+ qint64 getRemainingTime(const QDeadlineTimer &deadline) const;
+ bool poll(const QDeadlineTimer &deadline);
+
+ enum { maxHandles = 2 };
+ HANDLE handles[maxHandles];
+ DWORD handleCount = 0;
+ bool waitForClose = false;
+ bool writePending = false;
+};
+
+QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket)
+{
+ if (socket.pipeWriter && socket.pipeWriter->isWriteOperationActive()) {
+ handles[handleCount++] = socket.pipeWriter->syncEvent();
+ writePending = true;
+ }
+ if (socket.pipeReader->isReadOperationActive())
+ handles[handleCount++] = socket.pipeReader->syncEvent();
+ else
+ waitForClose = true;
+}
+
+qint64 QSocketPoller::getRemainingTime(const QDeadlineTimer &deadline) const
+{
+ const qint64 sleepTime = 10;
+ qint64 remainingTime = deadline.remainingTime();
+ if (waitForClose && (remainingTime > sleepTime || remainingTime == -1))
+ return sleepTime;
+
+ return remainingTime;
+}
+
+/*!
+ \internal
+
+ Waits until new data is available for reading or write operation
+ completes. Returns \c true, if we need to check pipe workers;
+ otherwise it returns \c false (if an error occurred or the operation
+ timed out).
+
+ \note If the read operation is inactive, it succeeds after
+ a short wait, allowing the caller to check the state of the socket.
+*/
+bool QSocketPoller::poll(const QDeadlineTimer &deadline)
+{
+ Q_ASSERT(handleCount != 0);
+ QDeadlineTimer timer(getRemainingTime(deadline));
+ DWORD waitRet;
+
+ do {
+ waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
+ timer.remainingTime(), TRUE);
+ } while (waitRet == WAIT_IO_COMPLETION);
+
+ if (waitRet == WAIT_TIMEOUT)
+ return waitForClose || !deadline.hasExpired();
+
+ return waitRet - WAIT_OBJECT_0 < handleCount;
+}
+} // anonymous namespace
+
void QLocalSocketPrivate::init()
{
Q_Q(QLocalSocket);
pipeReader = new QWindowsPipeReader(q);
- q->connect(pipeReader, SIGNAL(readyRead()), SIGNAL(readyRead()));
+ QObjectPrivate::connect(pipeReader, &QWindowsPipeReader::readyRead,
+ this, &QLocalSocketPrivate::_q_canRead);
q->connect(pipeReader, SIGNAL(pipeClosed()), SLOT(_q_pipeClosed()), Qt::QueuedConnection);
q->connect(pipeReader, SIGNAL(winError(ulong,QString)), SLOT(_q_winError(ulong,QString)));
}
@@ -91,7 +126,7 @@ void QLocalSocketPrivate::_q_winError(ulong windowsError, const QString &functio
if (state == QLocalSocket::UnconnectedState && currentState != QLocalSocket::ConnectingState)
emit q->disconnected();
}
- emit q->error(error);
+ emit q->errorOccurred(error);
}
QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
@@ -99,22 +134,17 @@ QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
pipeWriter(0),
pipeReader(0),
error(QLocalSocket::UnknownSocketError),
- state(QLocalSocket::UnconnectedState)
+ state(QLocalSocket::UnconnectedState),
+ emittedReadyRead(false),
+ emittedBytesWritten(false)
{
- writeBufferChunkSize = QIODEVICE_BUFFERSIZE;
}
QLocalSocketPrivate::~QLocalSocketPrivate()
{
- destroyPipeHandles();
-}
-
-void QLocalSocketPrivate::destroyPipeHandles()
-{
- if (handle != INVALID_HANDLE_VALUE) {
- DisconnectNamedPipe(handle);
- CloseHandle(handle);
- }
+ Q_ASSERT(state == QLocalSocket::UnconnectedState);
+ Q_ASSERT(handle == INVALID_HANDLE_VALUE);
+ Q_ASSERT(pipeWriter == nullptr);
}
void QLocalSocket::connectToServer(OpenMode openMode)
@@ -123,7 +153,7 @@ void QLocalSocket::connectToServer(OpenMode openMode)
if (state() == ConnectedState || state() == ConnectingState) {
d->error = OperationError;
d->errorString = tr("Trying to connect while connection is in progress");
- emit error(QLocalSocket::OperationError);
+ emit errorOccurred(QLocalSocket::OperationError);
return;
}
@@ -133,14 +163,14 @@ void QLocalSocket::connectToServer(OpenMode openMode)
emit stateChanged(d->state);
if (d->serverName.isEmpty()) {
d->error = ServerNotFoundError;
- d->errorString = tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer"));
+ d->errorString = tr("%1: Invalid name").arg("QLocalSocket::connectToServer"_L1);
d->state = UnconnectedState;
- emit error(d->error);
+ emit errorOccurred(d->error);
emit stateChanged(d->state);
return;
}
- const QLatin1String pipePath("\\\\.\\pipe\\");
+ const auto pipePath = "\\\\.\\pipe\\"_L1;
if (d->serverName.startsWith(pipePath))
d->fullServerName = d->serverName;
else
@@ -173,7 +203,7 @@ void QLocalSocket::connectToServer(OpenMode openMode)
if (localSocket == INVALID_HANDLE_VALUE) {
const DWORD winError = GetLastError();
- d->_q_winError(winError, QLatin1String("QLocalSocket::connectToServer"));
+ d->_q_winError(winError, "QLocalSocket::connectToServer"_L1);
d->fullServerName = QString();
return;
}
@@ -183,6 +213,20 @@ void QLocalSocket::connectToServer(OpenMode openMode)
emit connected();
}
+static qint64 transformPipeReaderResult(qint64 res)
+{
+ // QWindowsPipeReader's reading functions return error codes
+ // that don't match what we need.
+ switch (res) {
+ case 0: // EOF -> transform to error
+ return -1;
+ case -2: // EWOULDBLOCK -> no error, just no bytes
+ return 0;
+ default:
+ return res;
+ }
+}
+
// This is reading from the buffer
qint64 QLocalSocket::readData(char *data, qint64 maxSize)
{
@@ -191,33 +235,56 @@ qint64 QLocalSocket::readData(char *data, qint64 maxSize)
if (!maxSize)
return 0;
- qint64 ret = d->pipeReader->read(data, maxSize);
+ return transformPipeReaderResult(d->pipeReader->read(data, maxSize));
+}
- // QWindowsPipeReader::read() returns error codes that don't match what we need
- switch (ret) {
- case 0: // EOF -> transform to error
- return -1;
- case -2: // EWOULDBLOCK -> no error, just no bytes
+qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
+{
+ Q_D(QLocalSocket);
+
+ if (!maxSize)
return 0;
- default:
- return ret;
- }
+
+ // QIODevice::readLine() reserves space for the trailing '\0' byte,
+ // so we must read 'maxSize + 1' bytes.
+ return transformPipeReaderResult(d->pipeReader->readLine(data, maxSize + 1));
+}
+
+qint64 QLocalSocket::skipData(qint64 maxSize)
+{
+ Q_D(QLocalSocket);
+
+ if (!maxSize)
+ return 0;
+
+ return transformPipeReaderResult(d->pipeReader->skip(maxSize));
}
qint64 QLocalSocket::writeData(const char *data, qint64 len)
{
Q_D(QLocalSocket);
+ if (!isValid()) {
+ d->error = OperationError;
+ d->errorString = tr("Socket is not connected");
+ return -1;
+ }
+
if (len == 0)
return 0;
- d->writeBuffer.append(data, len);
+
if (!d->pipeWriter) {
d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
- connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,
- this, &QLocalSocket::bytesWritten);
- QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::canWrite,
- d, &QLocalSocketPrivate::_q_canWrite);
+ QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,
+ d, &QLocalSocketPrivate::_q_bytesWritten);
+ QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::writeFailed,
+ d, &QLocalSocketPrivate::_q_writeFailed);
}
- d->_q_canWrite();
+
+ if (d->isWriteChunkCached(data, len))
+ d->pipeWriter->write(*(d->currentWriteChunk));
+ else
+ d->pipeWriter->write(data, len);
+
return len;
}
@@ -231,31 +298,43 @@ void QLocalSocket::abort()
close();
}
+void QLocalSocketPrivate::_q_canRead()
+{
+ Q_Q(QLocalSocket);
+ if (!emittedReadyRead) {
+ QScopedValueRollback<bool> guard(emittedReadyRead, true);
+ emit q->readyRead();
+ }
+}
+
void QLocalSocketPrivate::_q_pipeClosed()
{
Q_Q(QLocalSocket);
if (state == QLocalSocket::UnconnectedState)
return;
- emit q->readChannelFinished();
if (state != QLocalSocket::ClosingState) {
state = QLocalSocket::ClosingState;
emit q->stateChanged(state);
if (state != QLocalSocket::ClosingState)
return;
}
- state = QLocalSocket::UnconnectedState;
- emit q->stateChanged(state);
- emit q->disconnected();
+ serverName.clear();
+ fullServerName.clear();
pipeReader->stop();
- destroyPipeHandles();
- handle = INVALID_HANDLE_VALUE;
-
- if (pipeWriter) {
- delete pipeWriter;
- pipeWriter = 0;
+ delete pipeWriter;
+ pipeWriter = nullptr;
+ if (handle != INVALID_HANDLE_VALUE) {
+ DisconnectNamedPipe(handle);
+ CloseHandle(handle);
+ handle = INVALID_HANDLE_VALUE;
}
+
+ state = QLocalSocket::UnconnectedState;
+ emit q->stateChanged(state);
+ emit q->readChannelFinished();
+ emit q->disconnected();
}
qint64 QLocalSocket::bytesAvailable() const
@@ -269,7 +348,7 @@ qint64 QLocalSocket::bytesAvailable() const
qint64 QLocalSocket::bytesToWrite() const
{
Q_D(const QLocalSocket);
- return d->writeBuffer.size() + (d->pipeWriter ? d->pipeWriter->bytesToWrite() : 0);
+ return d->pipeWriterBytesToWrite();
}
bool QLocalSocket::canReadLine() const
@@ -281,52 +360,30 @@ bool QLocalSocket::canReadLine() const
void QLocalSocket::close()
{
Q_D(QLocalSocket);
- if (openMode() == NotOpen)
- return;
- d->setWriteChannelCount(0);
QIODevice::close();
+ d->pipeReader->stopAndClear();
d->serverName = QString();
d->fullServerName = QString();
-
- if (state() != UnconnectedState) {
- if (bytesToWrite() > 0) {
- disconnectFromServer();
- return;
- }
-
- d->_q_pipeClosed();
- }
+ disconnectFromServer();
}
bool QLocalSocket::flush()
{
Q_D(QLocalSocket);
- bool written = false;
- while (d->pipeWriter && d->pipeWriter->waitForWrite(0))
- written = true;
- return written;
+
+ return d->pipeWriter && d->pipeWriter->checkForWrite();
}
void QLocalSocket::disconnectFromServer()
{
Q_D(QLocalSocket);
- // Are we still connected?
- if (!isValid()) {
- // If we have unwritten data, the pipeWriter is still present.
- // It must be destroyed before close() to prevent an infinite loop.
- delete d->pipeWriter;
- d->pipeWriter = 0;
- d->writeBuffer.clear();
- }
-
- flush();
- if (bytesToWrite() != 0) {
+ if (bytesToWrite() == 0) {
+ d->_q_pipeClosed();
+ } else if (d->state != QLocalSocket::ClosingState) {
d->state = QLocalSocket::ClosingState;
emit stateChanged(d->state);
- } else {
- close();
}
}
@@ -351,17 +408,30 @@ bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
return true;
}
-void QLocalSocketPrivate::_q_canWrite()
+qint64 QLocalSocketPrivate::pipeWriterBytesToWrite() const
+{
+ return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
+}
+
+void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes)
{
Q_Q(QLocalSocket);
- if (writeBuffer.isEmpty()) {
- if (state == QLocalSocket::ClosingState)
- q->close();
- } else {
- Q_ASSERT(pipeWriter);
- if (!pipeWriter->isWriteOperationActive())
- pipeWriter->write(writeBuffer.read());
+ if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> guard(emittedBytesWritten, true);
+ emit q->bytesWritten(bytes);
}
+ if (state == QLocalSocket::ClosingState)
+ q->disconnectFromServer();
+}
+
+void QLocalSocketPrivate::_q_writeFailed()
+{
+ Q_Q(QLocalSocket);
+ error = QLocalSocket::PeerClosedError;
+ errorString = QLocalSocket::tr("Remote closed");
+ emit q->errorOccurred(error);
+
+ _q_pipeClosed();
}
qintptr QLocalSocket::socketDescriptor() const
@@ -395,15 +465,38 @@ bool QLocalSocket::waitForDisconnected(int msecs)
qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
return false;
}
- if (!openMode().testFlag(QIODevice::ReadOnly)) {
- qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes.");
- return false;
- }
- if (d->pipeReader->waitForPipeClosed(msecs)) {
- d->_q_pipeClosed();
- return true;
+
+ QDeadlineTimer deadline(msecs);
+ bool wasChecked = false;
+ while (!d->pipeReader->isPipeClosed()) {
+ if (wasChecked && deadline.hasExpired())
+ return false;
+
+ QSocketPoller poller(*d);
+ // The first parameter of the WaitForMultipleObjectsEx() call cannot
+ // be zero. So we have to call SleepEx() here.
+ if (!poller.writePending && poller.waitForClose) {
+ // Prevent waiting on the first pass, if both the pipe reader
+ // and the pipe writer are inactive.
+ if (wasChecked)
+ SleepEx(poller.getRemainingTime(deadline), TRUE);
+ } else if (!poller.poll(deadline)) {
+ return false;
+ }
+
+ if (d->pipeWriter)
+ d->pipeWriter->checkForWrite();
+
+ // When the read buffer is full, the read sequence is not running,
+ // so we need to peek the pipe to detect disconnection.
+ if (poller.waitForClose && isValid())
+ d->pipeReader->checkPipeState();
+
+ d->pipeReader->checkForReadyRead();
+ wasChecked = true;
}
- return false;
+ d->_q_pipeClosed();
+ return true;
}
bool QLocalSocket::isValid() const
@@ -419,32 +512,51 @@ bool QLocalSocket::waitForReadyRead(int msecs)
if (d->state != QLocalSocket::ConnectedState)
return false;
- // We already know that the pipe is gone, but did not enter the event loop yet.
- if (d->pipeReader->isPipeClosed()) {
- d->_q_pipeClosed();
- return false;
- }
-
- bool result = d->pipeReader->waitForReadyRead(msecs);
+ QDeadlineTimer deadline(msecs);
+ while (!d->pipeReader->isPipeClosed()) {
+ QSocketPoller poller(*d);
+ if (poller.waitForClose || !poller.poll(deadline))
+ return false;
- // We just noticed that the pipe is gone.
- if (d->pipeReader->isPipeClosed())
- d->_q_pipeClosed();
+ if (d->pipeWriter)
+ d->pipeWriter->checkForWrite();
- return result;
+ if (d->pipeReader->checkForReadyRead())
+ return true;
+ }
+ d->_q_pipeClosed();
+ return false;
}
bool QLocalSocket::waitForBytesWritten(int msecs)
{
- Q_D(const QLocalSocket);
- if (!d->pipeWriter)
+ Q_D(QLocalSocket);
+
+ if (d->state == QLocalSocket::UnconnectedState)
return false;
- // Wait for the pipe writer to acknowledge that it has
- // written. This will succeed if either the pipe writer has
- // already written the data, or if it manages to write data
- // within the given timeout.
- return d->pipeWriter->waitForWrite(msecs);
+ QDeadlineTimer deadline(msecs);
+ bool wasChecked = false;
+ while (!d->pipeReader->isPipeClosed()) {
+ if (wasChecked && deadline.hasExpired())
+ return false;
+
+ QSocketPoller poller(*d);
+ if (!poller.writePending || !poller.poll(deadline))
+ return false;
+
+ Q_ASSERT(d->pipeWriter);
+ if (d->pipeWriter->checkForWrite())
+ return true;
+
+ if (poller.waitForClose && isValid())
+ d->pipeReader->checkPipeState();
+
+ d->pipeReader->checkForReadyRead();
+ wasChecked = true;
+ }
+ d->_q_pipeClosed();
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index 31628846dc..4c8b3ebf3f 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNATIVESOCKETENGINE_DEBUG
@@ -114,7 +78,7 @@
\sa readDatagram(), QNetworkDatagram
*/
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
@@ -135,6 +99,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
//#define QNATIVESOCKETENGINE_DEBUG
#define Q_VOID
@@ -195,7 +161,7 @@ QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() :
writeNotifier(nullptr),
exceptNotifier(nullptr)
{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN)
QSysInfo::machineHostName(); // this initializes ws2_32.dll
#endif
}
@@ -448,13 +414,13 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
// Create the socket
if (!d->createNewSocket(socketType, protocol)) {
#if defined (QNATIVESOCKETENGINE_DEBUG)
- QString typeStr = QLatin1String("UnknownSocketType");
- if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
- else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
- else if (socketType == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket");
- QString protocolStr = QLatin1String("UnknownProtocol");
- if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
- else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
+ QString typeStr = "UnknownSocketType"_L1;
+ if (socketType == QAbstractSocket::TcpSocket) typeStr = "TcpSocket"_L1;
+ else if (socketType == QAbstractSocket::UdpSocket) typeStr = "UdpSocket"_L1;
+ else if (socketType == QAbstractSocket::SctpSocket) typeStr = "SctpSocket"_L1;
+ QString protocolStr = "UnknownProtocol"_L1;
+ if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = "IPv4Protocol"_L1;
+ else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = "IPv6Protocol"_L1;
qDebug("QNativeSocketEngine::initialize(type == %s, protocol == %s) failed: %s",
typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(), d->socketErrorString.toLatin1().constData());
#endif
@@ -476,16 +442,18 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
}
+#ifndef Q_OS_WASM
// Make sure we receive out-of-band data
if (socketType == QAbstractSocket::TcpSocket
&& !setOption(ReceiveOutOfBandData, 1)) {
qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
}
+#endif
// Before Qt 4.6, we always set the send and receive buffer size to 49152 as
// this was found to be an optimal value. However, modern OS
// all have some kind of auto tuning for this and we therefore don't set
- // this explictly anymore.
+ // this explicitly anymore.
// If it introduces any performance regressions for Qt 4.6.x (x > 0) then
// it will be put back in.
//
@@ -689,7 +657,7 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
\sa bind(), accept()
*/
-bool QNativeSocketEngine::listen()
+bool QNativeSocketEngine::listen(int backlog)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
@@ -701,11 +669,7 @@ bool QNativeSocketEngine::listen()
Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
#endif
- // We're using a backlog of 50. Most modern kernels support TCP
- // syncookies by default, and if they do, the backlog is ignored.
- // When there is no support for TCP syncookies, this value is
- // fine.
- return d->nativeListen(50);
+ return d->nativeListen(backlog);
}
/*!
@@ -715,7 +679,7 @@ bool QNativeSocketEngine::listen()
\sa bind(), listen()
*/
-int QNativeSocketEngine::accept()
+qintptr QNativeSocketEngine::accept()
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
@@ -972,7 +936,7 @@ void QNativeSocketEngine::close()
if (d->exceptNotifier)
d->exceptNotifier->setEnabled(false);
- if(d->socketDescriptor != -1) {
+ if (d->socketDescriptor != -1) {
d->nativeClose();
d->socketDescriptor = -1;
}
@@ -984,23 +948,23 @@ void QNativeSocketEngine::close()
d->peerAddress.clear();
d->inboundStreamCount = d->outboundStreamCount = 0;
if (d->readNotifier) {
- qDeleteInEventHandler(d->readNotifier);
+ delete d->readNotifier;
d->readNotifier = nullptr;
}
if (d->writeNotifier) {
- qDeleteInEventHandler(d->writeNotifier);
+ delete d->writeNotifier;
d->writeNotifier = nullptr;
}
if (d->exceptNotifier) {
- qDeleteInEventHandler(d->exceptNotifier);
+ delete d->exceptNotifier;
d->exceptNotifier = nullptr;
}
}
/*!
- Waits for \a msecs milliseconds or until the socket is ready for
- reading. If \a timedOut is not \nullptr and \a msecs milliseconds
- have passed, the value of \a timedOut is set to true.
+ Waits until \a deadline has expired or until the socket is ready for
+ reading. If \a timedOut is not \nullptr and \a deadline has expired,
+ the value of \a timedOut is set to true.
Returns \c true if data is available for reading; otherwise returns
false.
@@ -1012,7 +976,7 @@ void QNativeSocketEngine::close()
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
-bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QNativeSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
@@ -1022,7 +986,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
- int ret = d->nativeSelect(msecs, true);
+ int ret = d->nativeSelect(deadline, true);
if (ret == 0) {
if (timedOut)
*timedOut = true;
@@ -1038,9 +1002,9 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
}
/*!
- Waits for \a msecs milliseconds or until the socket is ready for
- writing. If \a timedOut is not \nullptr and \a msecs milliseconds
- have passed, the value of \a timedOut is set to true.
+ Waits until \a deadline has expired or until the socket is ready for
+ writing. If \a timedOut is not \nullptr and \a deadline has expired,
+ the value of \a timedOut is set to true.
Returns \c true if data is available for writing; otherwise returns
false.
@@ -1052,7 +1016,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
-bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QNativeSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
@@ -1062,7 +1026,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
- int ret = d->nativeSelect(msecs, false);
+ int ret = d->nativeSelect(deadline, false);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
@@ -1110,14 +1074,14 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false);
Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
QAbstractSocket::UnconnectedState, false);
- int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite);
+ int ret = d->nativeSelect(deadline, checkRead, checkWrite, readyToRead, readyToWrite);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
@@ -1291,7 +1255,7 @@ bool QReadNotifier::event(QEvent *e)
class QWriteNotifier : public QSocketNotifier
{
public:
- QWriteNotifier(int fd, QNativeSocketEngine *parent)
+ QWriteNotifier(qintptr fd, QNativeSocketEngine *parent)
: QSocketNotifier(fd, QSocketNotifier::Write, parent) { engine = parent; }
protected:
@@ -1315,7 +1279,7 @@ bool QWriteNotifier::event(QEvent *e)
class QExceptionNotifier : public QSocketNotifier
{
public:
- QExceptionNotifier(int fd, QNativeSocketEngine *parent)
+ QExceptionNotifier(qintptr fd, QNativeSocketEngine *parent)
: QSocketNotifier(fd, QSocketNotifier::Exception, parent) { engine = parent; }
protected:
@@ -1382,3 +1346,5 @@ void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable)
}
QT_END_NAMESPACE
+
+#include "moc_qnativesocketengine_p.cpp"
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index e5f0701d14..4c185b7a4a 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNATIVESOCKETENGINE_P_H
#define QNATIVESOCKETENGINE_P_H
@@ -56,8 +20,9 @@
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qnetworkinterface.h"
#include "private/qabstractsocketengine_p.h"
+#include "qplatformdefs.h"
+
#ifndef Q_OS_WIN
-# include "qplatformdefs.h"
# include <netinet/in.h>
#else
# include <winsock2.h>
@@ -70,51 +35,63 @@ QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN
# define QT_SOCKLEN_T int
# define QT_SOCKOPTLEN_T int
-
-// The following definitions are copied from the MinGW header mswsock.h which
-// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
-// were introduced with Windows Vista, so some Win32 headers are lacking them.
-// There are no known versions of Windows CE or Embedded that contain them.
-# ifndef WSAID_WSARECVMSG
-typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
- LPDWORD lpdwNumberOfBytesRecvd,
- LPWSAOVERLAPPED lpOverlapped,
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
-# endif // !WSAID_WSARECVMSG
-# ifndef WSAID_WSASENDMSG
-typedef struct {
- LPWSAMSG lpMsg;
- DWORD dwFlags;
- LPDWORD lpNumberOfBytesSent;
- LPWSAOVERLAPPED lpOverlapped;
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
-} WSASENDMSG, *LPWSASENDMSG;
-
-typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
- LPDWORD lpNumberOfBytesSent,
- LPWSAOVERLAPPED lpOverlapped,
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-
-# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
-# endif // !WSAID_WSASENDMSG
-#endif // Q_OS_WIN
-
-union qt_sockaddr {
- sockaddr a;
- sockaddr_in a4;
- sockaddr_in6 a6;
-};
+#endif
namespace {
namespace SetSALen {
template <typename T> void set(T *sa, typename std::enable_if<(&T::sa_len, true), QT_SOCKLEN_T>::type len)
{ sa->sa_len = len; }
+ template <typename T> void set(T *sa, typename std::enable_if<(&T::sin_len, true), QT_SOCKLEN_T>::type len)
+ { sa->sin_len = len; }
template <typename T> void set(T *sin6, typename std::enable_if<(&T::sin6_len, true), QT_SOCKLEN_T>::type len)
{ sin6->sin6_len = len; }
template <typename T> void set(T *, ...) {}
}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr_in *sin, const QHostAddress &addr, quint16 port = 0)
+{
+ *sin = {};
+ SetSALen::set(sin, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_addr.s_addr = htonl(addr.toIPv4Address());
+ return sizeof(*sin);
+}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr_in6 *sin6, const QHostAddress &addr, quint16 port = 0)
+{
+ *sin6 = {};
+ SetSALen::set(sin6, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ memcpy(sin6->sin6_addr.s6_addr, addr.toIPv6Address().c, sizeof(sin6->sin6_addr));
+#if QT_CONFIG(networkinterface)
+ sin6->sin6_scope_id = QNetworkInterface::interfaceIndexFromName(addr.scopeId());
+#else
+ // it had better be a number then, if it is not empty
+ sin6->sin6_scope_id = addr.scopeId().toUInt();
+#endif
+ return sizeof(*sin6);
+}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr *sa, const QHostAddress &addr, quint16 port = 0)
+{
+ switch (addr.protocol()) {
+ case QHostAddress::IPv4Protocol:
+ return setSockaddr(reinterpret_cast<sockaddr_in *>(sa), addr, port);
+
+ case QHostAddress::IPv6Protocol:
+ case QHostAddress::AnyIPProtocol:
+ return setSockaddr(reinterpret_cast<sockaddr_in6 *>(sa), addr, port);
+
+ case QHostAddress::UnknownNetworkLayerProtocol:
+ break;
+ }
+ *sa = {};
+ sa->sa_family = AF_UNSPEC;
+ return 0;
}
+} // unnamed namespace
class QNativeSocketEnginePrivate;
#ifndef QT_NO_NETWORKINTERFACE
@@ -138,8 +115,8 @@ public:
bool connectToHost(const QHostAddress &address, quint16 port) override;
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
- bool listen() override;
- int accept() override;
+ bool listen(int backlog) override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -177,11 +154,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -199,132 +179,6 @@ private:
Q_DISABLE_COPY_MOVE(QNativeSocketEngine)
};
-class QSocketNotifier;
-
-class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
-{
- Q_DECLARE_PUBLIC(QNativeSocketEngine)
-public:
- QNativeSocketEnginePrivate();
- ~QNativeSocketEnginePrivate();
-
- qintptr socketDescriptor;
-
- QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
-
-#if defined(Q_OS_WIN)
- LPFN_WSASENDMSG sendmsg;
- LPFN_WSARECVMSG recvmsg;
-# endif
- enum ErrorString {
- NonBlockingInitFailedErrorString,
- BroadcastingInitFailedErrorString,
- NoIpV6ErrorString,
- RemoteHostClosedErrorString,
- TimeOutErrorString,
- ResourceErrorString,
- OperationUnsupportedErrorString,
- ProtocolUnsupportedErrorString,
- InvalidSocketErrorString,
- HostUnreachableErrorString,
- NetworkUnreachableErrorString,
- AccessErrorString,
- ConnectionTimeOutErrorString,
- ConnectionRefusedErrorString,
- AddressInuseErrorString,
- AddressNotAvailableErrorString,
- AddressProtectedErrorString,
- DatagramTooLargeErrorString,
- SendDatagramErrorString,
- ReceiveDatagramErrorString,
- WriteErrorString,
- ReadErrorString,
- PortInuseErrorString,
- NotSocketErrorString,
- InvalidProxyTypeString,
- TemporaryErrorString,
- NetworkDroppedConnectionErrorString,
- ConnectionResetErrorString,
-
- UnknownSocketErrorString = -1
- };
-
- void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
- QHostAddress adjustAddressProtocol(const QHostAddress &address) const;
-
- // native functions
- int option(QNativeSocketEngine::SocketOption option) const;
- bool setOption(QNativeSocketEngine::SocketOption option, int value);
-
- bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
-
- bool nativeConnect(const QHostAddress &address, quint16 port);
- bool nativeBind(const QHostAddress &address, quint16 port);
- bool nativeListen(int backlog);
- int nativeAccept();
-#ifndef QT_NO_NETWORKINTERFACE
- bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- QNetworkInterface nativeMulticastInterface() const;
- bool nativeSetMulticastInterface(const QNetworkInterface &iface);
-#endif
- qint64 nativeBytesAvailable() const;
-
- bool nativeHasPendingDatagrams() const;
- qint64 nativePendingDatagramSize() const;
- qint64 nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
- QAbstractSocketEngine::PacketHeaderOptions options);
- qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header);
- qint64 nativeRead(char *data, qint64 maxLength);
- qint64 nativeWrite(const char *data, qint64 length);
- int nativeSelect(int timeout, bool selectForRead) const;
- int nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const;
-
- void nativeClose();
-
- bool checkProxy(const QHostAddress &address);
- bool fetchConnectionParameters();
-
-#if QT_CONFIG(networkinterface)
- static uint scopeIdFromString(const QString &scopeid)
- { return QNetworkInterface::interfaceIndexFromName(scopeid); }
-#endif
-
- /*! \internal
- Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize.
- The address \a is converted to IPv6 if the current socket protocol is also IPv6.
- */
- void setPortAndAddress(quint16 port, const QHostAddress &address, qt_sockaddr *aa, QT_SOCKLEN_T *sockAddrSize)
- {
- if (address.protocol() == QAbstractSocket::IPv6Protocol
- || address.protocol() == QAbstractSocket::AnyIPProtocol
- || socketProtocol == QAbstractSocket::IPv6Protocol
- || socketProtocol == QAbstractSocket::AnyIPProtocol) {
- memset(&aa->a6, 0, sizeof(sockaddr_in6));
- aa->a6.sin6_family = AF_INET6;
-#if QT_CONFIG(networkinterface)
- aa->a6.sin6_scope_id = scopeIdFromString(address.scopeId());
-#endif
- aa->a6.sin6_port = htons(port);
- Q_IPV6ADDR tmp = address.toIPv6Address();
- memcpy(&aa->a6.sin6_addr, &tmp, sizeof(tmp));
- *sockAddrSize = sizeof(sockaddr_in6);
- SetSALen::set(&aa->a, sizeof(sockaddr_in6));
- } else {
- memset(&aa->a, 0, sizeof(sockaddr_in));
- aa->a4.sin_family = AF_INET;
- aa->a4.sin_port = htons(port);
- aa->a4.sin_addr.s_addr = htonl(address.toIPv4Address());
- *sockAddrSize = sizeof(sockaddr_in);
- SetSALen::set(&aa->a, sizeof(sockaddr_in));
- }
- }
-
-};
-
QT_END_NAMESPACE
#endif // QNATIVESOCKETENGINE_P_H
diff --git a/src/network/socket/qnativesocketengine_p_p.h b/src/network/socket/qnativesocketengine_p_p.h
new file mode 100644
index 0000000000..189a4327fd
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_p_p.h
@@ -0,0 +1,189 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNATIVESOCKETENGINE_P_P_H
+#define QNATIVESOCKETENGINE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractsocketengine_p.h"
+#include "private/qnativesocketengine_p.h"
+
+#ifndef Q_OS_WIN
+# include <netinet/in.h>
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <mswsock.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_WIN
+
+// The following definitions are copied from the MinGW header mswsock.h which
+// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
+// were introduced with Windows Vista, so some Win32 headers are lacking them.
+// There are no known versions of Windows CE or Embedded that contain them.
+# ifndef WSAID_WSARECVMSG
+typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
+ LPDWORD lpdwNumberOfBytesRecvd,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
+# endif // !WSAID_WSARECVMSG
+# ifndef WSAID_WSASENDMSG
+typedef struct {
+ LPWSAMSG lpMsg;
+ DWORD dwFlags;
+ LPDWORD lpNumberOfBytesSent;
+ LPWSAOVERLAPPED lpOverlapped;
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
+} WSASENDMSG, *LPWSASENDMSG;
+
+typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
+ LPDWORD lpNumberOfBytesSent,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+
+# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
+# endif // !WSAID_WSASENDMSG
+#endif // Q_OS_WIN
+
+union qt_sockaddr {
+ sockaddr a;
+ sockaddr_in a4;
+ sockaddr_in6 a6;
+};
+
+class QSocketNotifier;
+
+class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QNativeSocketEngine)
+public:
+ QNativeSocketEnginePrivate();
+ ~QNativeSocketEnginePrivate();
+
+ qintptr socketDescriptor;
+
+ QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
+
+#if defined(Q_OS_WIN)
+ LPFN_WSASENDMSG sendmsg;
+ LPFN_WSARECVMSG recvmsg;
+# endif
+ enum ErrorString {
+ NonBlockingInitFailedErrorString,
+ BroadcastingInitFailedErrorString,
+ NoIpV6ErrorString,
+ RemoteHostClosedErrorString,
+ TimeOutErrorString,
+ ResourceErrorString,
+ OperationUnsupportedErrorString,
+ ProtocolUnsupportedErrorString,
+ InvalidSocketErrorString,
+ HostUnreachableErrorString,
+ NetworkUnreachableErrorString,
+ AccessErrorString,
+ ConnectionTimeOutErrorString,
+ ConnectionRefusedErrorString,
+ AddressInuseErrorString,
+ AddressNotAvailableErrorString,
+ AddressProtectedErrorString,
+ DatagramTooLargeErrorString,
+ SendDatagramErrorString,
+ ReceiveDatagramErrorString,
+ WriteErrorString,
+ ReadErrorString,
+ PortInuseErrorString,
+ NotSocketErrorString,
+ InvalidProxyTypeString,
+ TemporaryErrorString,
+ NetworkDroppedConnectionErrorString,
+ ConnectionResetErrorString,
+
+ UnknownSocketErrorString = -1
+ };
+
+ void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
+ QHostAddress adjustAddressProtocol(const QHostAddress &address) const;
+
+ // native functions
+ int option(QNativeSocketEngine::SocketOption option) const;
+ bool setOption(QNativeSocketEngine::SocketOption option, int value);
+
+ bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
+
+ bool nativeConnect(const QHostAddress &address, quint16 port);
+ bool nativeBind(const QHostAddress &address, quint16 port);
+ bool nativeListen(int backlog);
+ qintptr nativeAccept();
+#ifndef QT_NO_NETWORKINTERFACE
+ bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface nativeMulticastInterface() const;
+ bool nativeSetMulticastInterface(const QNetworkInterface &iface);
+#endif
+ qint64 nativeBytesAvailable() const;
+
+ bool nativeHasPendingDatagrams() const;
+ qint64 nativePendingDatagramSize() const;
+ qint64 nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
+ QAbstractSocketEngine::PacketHeaderOptions options);
+ qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header);
+ qint64 nativeRead(char *data, qint64 maxLength);
+ qint64 nativeWrite(const char *data, qint64 length);
+ int nativeSelect(QDeadlineTimer deadline, bool selectForRead) const;
+ int nativeSelect(QDeadlineTimer deadline, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const;
+
+ void nativeClose();
+
+ bool checkProxy(const QHostAddress &address);
+ bool fetchConnectionParameters();
+
+ /*! \internal
+ Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize.
+ The address \a is converted to IPv6 if the current socket protocol is also IPv6.
+ */
+ void setPortAndAddress(quint16 port, const QHostAddress &address, qt_sockaddr *aa, QT_SOCKLEN_T *sockAddrSize)
+ {
+ switch (socketProtocol) {
+ case QHostAddress::IPv6Protocol:
+ case QHostAddress::AnyIPProtocol:
+ // force to IPv6
+ setSockaddr(&aa->a6, address, port);
+ *sockAddrSize = sizeof(sockaddr_in6);
+ return;
+
+ case QHostAddress::IPv4Protocol:
+ // force to IPv4
+ setSockaddr(&aa->a4, address, port);
+ *sockAddrSize = sizeof(sockaddr_in);
+ return;
+
+ case QHostAddress::UnknownNetworkLayerProtocol:
+ // don't force
+ break;
+ }
+ *sockAddrSize = setSockaddr(&aa->a, address, port);
+ }
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QNATIVESOCKETENGINE_P_P_H
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index e5b9fbbdb2..b6df412253 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -1,70 +1,28 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNATIVESOCKETENGINE_DEBUG
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include "private/qnet_unix_p.h"
+#include "qdeadlinetimer.h"
#include "qiodevice.h"
#include "qhostaddress.h"
-#include "qelapsedtimer.h"
#include "qvarlengtharray.h"
#include "qnetworkinterface.h"
+#include "qendian.h"
+#ifdef Q_OS_WASM
+#include <private/qeventdispatcher_wasm_p.h>
+#endif
#include <time.h>
#include <errno.h>
#include <fcntl.h>
-#ifndef QT_NO_IPV6IFNAME
-#include <net/if.h>
-#endif
-#ifdef QT_LINUXBASE
-#include <arpa/inet.h>
-#endif
-#ifdef Q_OS_BSD4
-#include <net/if_dl.h>
-#endif
#ifdef Q_OS_INTEGRITY
#include <sys/uio.h>
#endif
#if defined QNATIVESOCKETENGINE_DEBUG
-#include <qstring.h>
-#include <ctype.h>
+#include <private/qdebug_p.h>
#endif
#include <netinet/tcp.h>
@@ -73,41 +31,12 @@
#include <sys/socket.h>
#include <netinet/sctp.h>
#endif
+#ifdef Q_OS_BSD4
+# include <net/if_dl.h>
+#endif
QT_BEGIN_NAMESPACE
-#if defined QNATIVESOCKETENGINE_DEBUG
-
-/*
- Returns a human readable representation of the first \a len
- characters in \a data.
-*/
-static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
-{
- if (!data) return "(null)";
- QByteArray out;
- for (int i = 0; i < len; ++i) {
- char c = data[i];
- if (isprint(c)) {
- out += c;
- } else switch (c) {
- case '\n': out += "\\n"; break;
- case '\r': out += "\\r"; break;
- case '\t': out += "\\t"; break;
- default:
- QString tmp;
- tmp.sprintf("\\%o", c);
- out += tmp.toLatin1();
- }
- }
-
- if (len < maxSize)
- out += "...";
-
- return out;
-}
-#endif
-
/*
Extracts the port and address from a sockaddr, and stores them in
\a port and \a addr if they are non-null.
@@ -312,10 +241,8 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
#endif
socketDescriptor = socket;
- if (socket != -1) {
- this->socketProtocol = socketProtocol;
- this->socketType = socketType;
- }
+ this->socketProtocol = socketProtocol;
+ this->socketType = socketType;
return true;
}
@@ -364,12 +291,12 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
}
int n, level;
- int v = -1;
+ int v = 0;
QT_SOCKOPTLEN_T len = sizeof(v);
convertToLevelAndOption(opt, socketProtocol, level, n);
if (n != -1 && ::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1)
- return v;
+ return len == 1 ? qFromUnaligned<quint8>(&v) : v;
return -1;
}
@@ -509,6 +436,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16
case EFAULT:
case ENOTSOCK:
socketState = QAbstractSocket::UnconnectedState;
+ break;
default:
break;
}
@@ -626,7 +554,7 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog)
return true;
}
-int QNativeSocketEnginePrivate::nativeAccept()
+qintptr QNativeSocketEnginePrivate::nativeAccept()
{
int acceptedDescriptor = qt_safe_accept(socketDescriptor, nullptr, nullptr);
if (acceptedDescriptor == -1) {
@@ -672,7 +600,7 @@ int QNativeSocketEnginePrivate::nativeAccept()
}
}
- return acceptedDescriptor;
+ return qintptr(acceptedDescriptor);
}
#ifndef QT_NO_NETWORKINTERFACE
@@ -785,17 +713,21 @@ QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const
return QNetworkInterface::interfaceFromIndex(v);
}
+#if defined(Q_OS_SOLARIS)
+ struct in_addr v = { 0, 0, 0, 0};
+#else
struct in_addr v = { 0 };
+#endif
QT_SOCKOPTLEN_T sizeofv = sizeof(v);
if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, &sizeofv) == -1)
return QNetworkInterface();
if (v.s_addr != 0 && sizeofv >= QT_SOCKOPTLEN_T(sizeof(v))) {
QHostAddress ipv4(ntohl(v.s_addr));
QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
- for (int i = 0; i < ifaces.count(); ++i) {
+ for (int i = 0; i < ifaces.size(); ++i) {
const QNetworkInterface &iface = ifaces.at(i);
QList<QNetworkAddressEntry> entries = iface.addressEntries();
- for (int j = 0; j < entries.count(); ++j) {
+ for (int j = 0; j < entries.size(); ++j) {
const QNetworkAddressEntry &entry = entries.at(j);
if (entry.ip() == ipv4)
return iface;
@@ -815,7 +747,7 @@ bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInter
struct in_addr v;
if (iface.isValid()) {
QList<QNetworkAddressEntry> entries = iface.addressEntries();
- for (int i = 0; i < entries.count(); ++i) {
+ for (int i = 0; i < entries.size(); ++i) {
const QNetworkAddressEntry &entry = entries.at(i);
const QHostAddress &ip = entry.ip();
if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
@@ -862,7 +794,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
// Peek 1 bytes into the next message.
ssize_t readBytes;
char c;
- EINTR_LOOP(readBytes, ::recv(socketDescriptor, &c, 1, MSG_PEEK));
+ QT_EINTR_LOOP(readBytes, ::recv(socketDescriptor, &c, 1, MSG_PEEK));
// If there's no error, or if our buffer was too small, there must be a
// pending datagram.
@@ -881,7 +813,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
#ifdef Q_OS_LINUX
// Linux can return the actual datagram size if we use MSG_TRUNC
char c;
- EINTR_LOOP(recvResult, ::recv(socketDescriptor, &c, 1, MSG_PEEK | MSG_TRUNC));
+ QT_EINTR_LOOP(recvResult, ::recv(socketDescriptor, &c, 1, MSG_PEEK | MSG_TRUNC));
#elif defined(SO_NREAD)
// macOS can return the actual datagram size if we use SO_NREAD
int value;
@@ -1044,7 +976,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
if (cmsgptr->cmsg_len == CMSG_LEN(sizeof(int))
&& ((cmsgptr->cmsg_level == IPPROTO_IPV6 && cmsgptr->cmsg_type == IPV6_HOPLIMIT)
|| (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) {
- Q_STATIC_ASSERT(sizeof(header->hopLimit) == sizeof(int));
+ static_assert(sizeof(header->hopLimit) == sizeof(int));
memcpy(&header->hopLimit, CMSG_DATA(cmsgptr), sizeof(header->hopLimit));
}
@@ -1061,7 +993,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %lli, %s, %i) == %lli",
- data, qt_prettyDebug(data, qMin(recvResult, ssize_t(16)), recvResult).data(), maxSize,
+ data, QtDebugUtils::toPrintable(data, recvResult, 16).constData(), maxSize,
(recvResult != -1 && options != QAbstractSocketEngine::WantNone)
? header->senderAddress.toString().toLatin1().constData() : "(unknown)",
(recvResult != -1 && options != QAbstractSocketEngine::WantNone)
@@ -1190,7 +1122,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEngine::sendDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli", data,
- qt_prettyDebug(data, qMin<int>(len, 16), len).data(), len,
+ QtDebugUtils::toPrintable(data, len, 16).constData(), len,
header.destinationAddress.toString().toLatin1().constData(),
header.destinationPort, (qint64) sentBytes);
#endif
@@ -1344,6 +1276,9 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
q->close();
break;
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
case EAGAIN:
writtenBytes = 0;
break;
@@ -1356,9 +1291,8 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %llu) == %i",
- data, qt_prettyDebug(data, qMin((int) len, 16),
- (int) len).data(), len, (int) writtenBytes);
+ qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %llu) == %i", data,
+ QtDebugUtils::toPrintable(data, len, 16).constData(), len, (int) writtenBytes);
#endif
return qint64(writtenBytes);
@@ -1407,22 +1341,24 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %llu) == %zd",
- data, qt_prettyDebug(data, qMin(r, ssize_t(16)), r).data(),
- maxSize, r);
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %llu) == %zd", data,
+ QtDebugUtils::toPrintable(data, r, 16).constData(), maxSize, r);
#endif
return qint64(r);
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool dummy;
- return nativeSelect(timeout, selectForRead, !selectForRead, &dummy, &dummy);
+ return nativeSelect(deadline, selectForRead, !selectForRead, &dummy, &dummy);
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const
+#ifndef Q_OS_WASM
+
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
+ bool checkWrite, bool *selectForRead,
+ bool *selectForWrite) const
{
pollfd pfd = qt_make_pollfd(socketDescriptor, 0);
@@ -1432,7 +1368,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
if (checkWrite)
pfd.events |= POLLOUT;
- const int ret = qt_poll_msecs(&pfd, 1, timeout);
+ const int ret = qt_safe_poll(&pfd, 1, deadline);
if (ret <= 0)
return ret;
@@ -1451,4 +1387,27 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
return ret;
}
+#else
+
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
+ bool checkWrite, bool *selectForRead,
+ bool *selectForWrite) const
+{
+ *selectForRead = checkRead;
+ *selectForWrite = checkWrite;
+ bool socketDisconnect = false;
+ QEventDispatcherWasm::socketSelect(deadline.remainingTime(), socketDescriptor, checkRead,
+ checkWrite, selectForRead, selectForWrite,
+ &socketDisconnect);
+
+ // The disconnect/close handling code in QAbstractsScket::canReadNotification()
+ // does not detect remote disconnect properly; do that here as a workardound.
+ if (socketDisconnect)
+ receiver->closeNotification();
+
+ return 1;
+}
+
+#endif // Q_OS_WASM
+
QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index dd115c33dc..6525f46e30 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -1,50 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// Prevent windows system header files from defining min/max as macros.
-#define NOMINMAX 1
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <winsock2.h>
#include <ws2tcpip.h>
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
@@ -52,13 +13,14 @@
#include <qdatetime.h>
#include <qnetworkinterface.h>
#include <qoperatingsystemversion.h>
+#include <qvarlengtharray.h>
#include <algorithm>
+#include <chrono>
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
-# include <qstring.h>
-# include <qbytearray.h>
+#include <private/qdebug_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -127,37 +89,7 @@ void verboseWSErrorDebug(int r)
qErrnoWarning(r, "more details");
}
-/*
- Returns a human readable representation of the first \a len
- characters in \a data.
-*/
-static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
-{
- if (!data) return "(null)";
- QByteArray out;
- for (int i = 0; i < len; ++i) {
- char c = data[i];
- if (isprint(int(uchar(c)))) {
- out += c;
- } else switch (c) {
- case '\n': out += "\\n"; break;
- case '\r': out += "\\r"; break;
- case '\t': out += "\\t"; break;
- default:
- QString tmp;
- tmp.sprintf("\\%o", c);
- out += tmp.toLatin1().constData();
- }
- }
-
- if (len < maxLength)
- out += "...";
-
- return out;
-}
-
-
-#define WS_ERROR_DEBUG(x) verboseWSErrorDebug(x);
+#define WS_ERROR_DEBUG(x) verboseWSErrorDebug(x)
#else
@@ -325,15 +257,6 @@ static inline QAbstractSocket::SocketType qt_socket_getType(qintptr socketDescri
bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol)
{
-
- //### no ip6 support on winsocket 1.1 but we will try not to use this !!!!!!!!!!!!1
- /*
- if (winsockVersion < 0x20 && socketProtocol == QAbstractSocket::IPv6Protocol) {
- //### no ip6 support
- return -1;
- }
- */
-
//### SCTP not implemented
if (socketType == QAbstractSocket::SctpSocket) {
setError(QAbstractSocket::UnsupportedSocketOperationError,
@@ -346,34 +269,15 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
|| (socketProtocol == QAbstractSocket::AnyIPProtocol)) ? AF_INET6 : AF_INET;
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
- // MSDN KB179942 states that on winnt 4 WSA_FLAG_OVERLAPPED is needed if socket is to be non blocking
- // and recomends alwasy doing it for cross windows version comapablity.
+ // MSDN KB179942 states that on winnt 4 WSA_FLAG_OVERLAPPED is needed if socket is to be non
+ // blocking and recommends always doing it for cross-windows-version compatibility.
- // WSA_FLAG_NO_HANDLE_INHERIT is atomic (like linux O_CLOEXEC), but requires windows 7 SP 1 or later
- // SetHandleInformation is supported since W2K but isn't atomic
+ // WSA_FLAG_NO_HANDLE_INHERIT is atomic (like linux O_CLOEXEC)
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
SOCKET socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
- // previous call fails if the windows 7 service pack 1 or hot fix isn't installed.
-
- // Try the old API if the new one failed on Windows 7
- if (socket == INVALID_SOCKET && QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) {
- socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
-#ifdef HANDLE_FLAG_INHERIT
- if (socket != INVALID_SOCKET) {
- // make non inheritable the old way
- BOOL handleFlags = SetHandleInformation(reinterpret_cast<HANDLE>(socket), HANDLE_FLAG_INHERIT, 0);
-#ifdef QNATIVESOCKETENGINE_DEBUG
- qDebug() << "QNativeSocketEnginePrivate::createNewSocket - set inheritable" << handleFlags;
-#else
- Q_UNUSED(handleFlags);
-#endif
- }
-#endif
- }
-
if (socket == INVALID_SOCKET) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
@@ -426,10 +330,8 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
sendmsg = 0;
socketDescriptor = socket;
- if (socket != INVALID_SOCKET) {
- this->socketProtocol = socketProtocol;
- this->socketType = socketType;
- }
+ this->socketProtocol = socketProtocol;
+ this->socketType = socketType;
// Make the socket nonblocking.
if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) {
@@ -510,7 +412,6 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
return false;
}
return true;
- break;
}
case QNativeSocketEngine::TypeOfServiceOption:
case QNativeSocketEngine::MaxStreamsSocketOption:
@@ -590,7 +491,8 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
// local address of the socket which bound on both IPv4 and IPv6 interfaces.
// This address does not match to any special address and should not be used
// to send the data. So, replace it with QHostAddress::Any.
- if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ const uchar ipv6MappedNet[] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 0,0,0,0};
+ if (localAddress.isInSubnet(QHostAddress(ipv6MappedNet), 128 - 32)) {
bool ok = false;
const quint32 localIPv4 = localAddress.toIPv4Address(&ok);
if (ok && localIPv4 == INADDR_ANY) {
@@ -869,10 +771,10 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog)
return true;
}
-int QNativeSocketEnginePrivate::nativeAccept()
+qintptr QNativeSocketEnginePrivate::nativeAccept()
{
- int acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0);
- if (acceptedDescriptor == -1) {
+ SOCKET acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0);
+ if (acceptedDescriptor == INVALID_SOCKET) {
int err = WSAGetLastError();
switch (err) {
case WSAEACCES:
@@ -906,19 +808,19 @@ int QNativeSocketEnginePrivate::nativeAccept()
setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString);
break;
}
- } else if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) {
+ } else if (acceptedDescriptor != INVALID_SOCKET && QAbstractEventDispatcher::instance()) {
// Because of WSAAsyncSelect() WSAAccept returns a non blocking socket
// with the same attributes as the listening socket including the current
// WSAAsyncSelect(). To be able to change the socket to blocking mode the
- // WSAAsyncSelect() call must be cancled.
+ // WSAAsyncSelect() call must be canceled.
QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read);
n.setEnabled(true);
n.setEnabled(false);
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor);
+ qDebug("QNativeSocketEnginePrivate::nativeAccept() == %lld", qint64(acceptedDescriptor));
#endif
- return acceptedDescriptor;
+ return qintptr(acceptedDescriptor);
}
static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d,
@@ -1281,7 +1183,7 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL
#if defined (QNATIVESOCKETENGINE_DEBUG)
bool printSender = (ret != -1 && (options & QNativeSocketEngine::WantDatagramSender) != 0);
qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %lli, %s, %i) == %lli",
- data, qt_prettyDebug(data, qMin<qint64>(ret, 16), ret).data(), maxLength,
+ data, QtDebugUtils::toPrintable(data, ret, 16).constData(), maxLength,
printSender ? header->senderAddress.toString().toLatin1().constData() : "(unknown)",
printSender ? header->senderPort : 0, ret);
#endif
@@ -1414,8 +1316,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeSendDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli", data,
- qt_prettyDebug(data, qMin<qint64>(len, 16), len).data(), len,
+ qDebug("QNativeSocketEnginePrivate::nativeSendDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli",
+ data, QtDebugUtils::toPrintable(data, len, 16).constData(), len,
header.destinationAddress.toString().toLatin1().constData(),
header.destinationPort, ret);
#endif
@@ -1443,10 +1345,8 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
int err;
if (socketRet != SOCKET_ERROR) {
- if (ret == len)
+ if (ret == len || bytesToSend != qint64(bytesWritten))
break;
- else
- continue;
} else if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
break;
} else if (err == WSAENOBUFS) {
@@ -1476,7 +1376,7 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %lli) == %lli",
- data, qt_prettyDebug(data, qMin(int(ret), 16), int(ret)).data(), len, ret);
+ data, QtDebugUtils::toPrintable(data, ret, 16).constData(), len, ret);
#endif
return ret;
@@ -1518,8 +1418,8 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
#if defined (QNATIVESOCKETENGINE_DEBUG)
if (ret != -2) {
- qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %lli) == %lli",
- data, qt_prettyDebug(data, qMin(int(bytesRead), 16), int(bytesRead)).data(), maxLength, ret);
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %lli) == %lli", data,
+ QtDebugUtils::toPrintable(data, bytesRead, 16).constData(), maxLength, ret);
} else {
qDebug("QNativeSocketEnginePrivate::nativeRead(%p, %lli) == -2 (WOULD BLOCK)",
data, maxLength);
@@ -1529,7 +1429,18 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
return ret;
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+inline timeval durationToTimeval(std::chrono::nanoseconds dur) noexcept
+{
+ using namespace std::chrono;
+ const auto secs = duration_cast<seconds>(dur);
+ const auto frac = duration_cast<microseconds>(dur - secs);
+ struct timeval tval;
+ tval.tv_sec = secs.count();
+ tval.tv_usec = frac.count();
+ return tval;
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool readEnabled = selectForRead && readNotifier && readNotifier->isEnabled();
if (readEnabled)
@@ -1543,12 +1454,10 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
fds.fd_count = 1;
fds.fd_array[0] = (SOCKET)socketDescriptor;
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
+ struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
if (selectForRead) {
- ret = select(0, &fds, 0, 0, timeout < 0 ? 0 : &tv);
+ ret = select(0, &fds, 0, 0, &tv);
} else {
// select for write
@@ -1557,7 +1466,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
FD_ZERO(&fdexception);
FD_SET((SOCKET)socketDescriptor, &fdexception);
- ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv);
+ ret = select(0, 0, &fds, &fdexception, &tv);
// ... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
@@ -1570,7 +1479,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
return ret;
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout,
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline,
bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const
{
@@ -1599,11 +1508,9 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
FD_SET((SOCKET)socketDescriptor, &fdexception);
}
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
+ struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
- ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
+ ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, &tv);
//... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp
deleted file mode 100644
index 2eb2141fee..0000000000
--- a/src/network/socket/qnativesocketengine_winrt.cpp
+++ /dev/null
@@ -1,1815 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qt_windows.h>
-
-#include "qnativesocketengine_winrt_p.h"
-
-#include <qcoreapplication.h>
-#include <qabstracteventdispatcher.h>
-#include <qsocketnotifier.h>
-#include <qdatetime.h>
-#include <qnetworkinterface.h>
-#include <qelapsedtimer.h>
-#include <qthread.h>
-#include <qabstracteventdispatcher.h>
-#include <qfunctions_winrt.h>
-
-#include <private/qthread_p.h>
-#include <private/qabstractsocket_p.h>
-#include <private/qeventdispatcher_winrt_p.h>
-
-#ifndef QT_NO_SSL
-#include <QSslSocket>
-#endif
-
-#include <functional>
-#include <wrl.h>
-#include <windows.foundation.collections.h>
-#include <windows.storage.streams.h>
-#include <windows.networking.h>
-#include <windows.networking.sockets.h>
-#include <robuffer.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Foundation::Collections;
-using namespace ABI::Windows::Storage::Streams;
-using namespace ABI::Windows::Networking;
-using namespace ABI::Windows::Networking::Connectivity;
-using namespace ABI::Windows::Networking::Sockets;
-#if _MSC_VER >= 1900
-using namespace ABI::Windows::Security::EnterpriseData;
-#endif
-
-typedef ITypedEventHandler<StreamSocketListener *, StreamSocketListenerConnectionReceivedEventArgs *> ClientConnectedHandler;
-typedef ITypedEventHandler<DatagramSocket *, DatagramSocketMessageReceivedEventArgs *> DatagramReceivedHandler;
-typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
-typedef IAsyncOperationWithProgressCompletedHandler<UINT32, UINT32> SocketWriteCompletedHandler;
-typedef IAsyncOperationWithProgress<IBuffer *, UINT32> IAsyncBufferOperation;
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(lcNetworkSocket, "qt.network.socket");
-Q_LOGGING_CATEGORY(lcNetworkSocketVerbose, "qt.network.socket.verbose");
-
-#if _MSC_VER >= 1900
-static HRESULT qt_winrt_try_create_thread_network_context(QString host, ComPtr<IThreadNetworkContext> &context)
-{
- HRESULT hr;
- ComPtr<IProtectionPolicyManagerStatics> protectionPolicyManager;
-
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_EnterpriseData_ProtectionPolicyManager).Get(),
- &protectionPolicyManager);
- RETURN_HR_IF_FAILED("Could not access ProtectionPolicyManager statics.");
-
- ComPtr<IHostNameFactory> hostNameFactory;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- RETURN_HR_IF_FAILED("Could not access HostName factory.");
-
- ComPtr<IHostName> hostName;
- HStringReference hostRef(reinterpret_cast<LPCWSTR>(host.utf16()), host.length());
- hr = hostNameFactory->CreateHostName(hostRef.Get(), &hostName);
- RETURN_HR_IF_FAILED("Could not create hostname.");
-
- ComPtr<IAsyncOperation<HSTRING>> op;
- hr = protectionPolicyManager->GetPrimaryManagedIdentityForNetworkEndpointAsync(hostName.Get(), &op);
- RETURN_HR_IF_FAILED("Could not get identity operation.");
-
- HSTRING hIdentity;
- hr = QWinRTFunctions::await(op, &hIdentity);
- RETURN_HR_IF_FAILED("Could not wait for identity operation.");
-
- // Implies there is no need for a network context for this address
- if (hIdentity == nullptr)
- return S_OK;
-
- hr = protectionPolicyManager->CreateCurrentThreadNetworkContext(hIdentity, &context);
- RETURN_HR_IF_FAILED("Could not create thread network context");
-
- return S_OK;
-}
-#endif // _MSC_VER >= 1900
-
-typedef QHash<qintptr, IStreamSocket *> TcpSocketHash;
-
-struct SocketHandler
-{
- SocketHandler() : socketCount(0) {}
- qintptr socketCount;
- TcpSocketHash pendingTcpSockets;
-};
-
-Q_GLOBAL_STATIC(SocketHandler, gSocketHandler)
-
-struct SocketGlobal
-{
- SocketGlobal()
- {
- HRESULT hr;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
- &bufferFactory);
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- ComPtr<IBufferFactory> bufferFactory;
-};
-Q_GLOBAL_STATIC(SocketGlobal, g)
-
-#define READ_BUFFER_SIZE 65536
-
-static inline QString qt_QStringFromHString(const HString &string)
-{
- UINT32 length;
- PCWSTR rawString = string.GetRawBuffer(&length);
- return QString::fromWCharArray(rawString, length);
-}
-
-class SocketEngineWorker : public QObject
-{
- Q_OBJECT
-public:
- SocketEngineWorker(QNativeSocketEnginePrivate *engine)
- : enginePrivate(engine)
- {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << engine;
- }
-
- ~SocketEngineWorker()
- {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- if (Q_UNLIKELY(initialReadOp)) {
- qCDebug(lcNetworkSocket) << Q_FUNC_INFO << "Closing initial read operation";
- ComPtr<IAsyncInfo> info;
- HRESULT hr = initialReadOp.As(&info);
- Q_ASSERT_SUCCEEDED(hr);
- if (info) {
- hr = info->Cancel();
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
- }
-
- if (readOp) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing read operation";
- ComPtr<IAsyncInfo> info;
- HRESULT hr = readOp.As(&info);
- Q_ASSERT_SUCCEEDED(hr);
- if (info) {
- hr = info->Cancel();
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
- }
-
- if (connectOp) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing connect operation";
- ComPtr<IAsyncInfo> info;
- HRESULT hr = connectOp.As(&info);
- Q_ASSERT_SUCCEEDED(hr);
- if (info) {
- hr = info->Cancel();
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
- }
- }
-
-signals:
- void connectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString);
- void newDataReceived();
- void socketErrorOccured(QAbstractSocket::SocketError error);
-
-public:
- void startReading()
- {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- ComPtr<IBuffer> buffer;
- HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IInputStream> stream;
- hr = tcpSocket->get_InputStream(&stream);
- Q_ASSERT_SUCCEEDED(hr);
- hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, initialReadOp.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- enginePrivate->socketState = QAbstractSocket::ConnectedState;
- hr = initialReadOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketEngineWorker::onReadyRead).Get());
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- HRESULT onConnectOpFinished(IAsyncAction *action, AsyncStatus)
- {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- HRESULT hr = action->GetResults();
- if (FAILED(hr)) {
- if (hr == HRESULT_FROM_WIN32(WSAETIMEDOUT)) {
- emit connectOpFinished(false, QAbstractSocket::NetworkError, WinRTSocketEngine::ConnectionTimeOutErrorString);
- return S_OK;
- } else if (hr == HRESULT_FROM_WIN32(WSAEHOSTUNREACH)) {
- emit connectOpFinished(false, QAbstractSocket::HostNotFoundError, WinRTSocketEngine::HostUnreachableErrorString);
- return S_OK;
- } else if (hr == HRESULT_FROM_WIN32(WSAECONNREFUSED)) {
- emit connectOpFinished(false, QAbstractSocket::ConnectionRefusedError, WinRTSocketEngine::ConnectionRefusedErrorString);
- return S_OK;
- } else {
- emit connectOpFinished(false, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
- return S_OK;
- }
- }
-
- // The callback might be triggered several times if we do not cancel/reset it here
- if (connectOp) {
- ComPtr<IAsyncInfo> info;
- hr = connectOp.As(&info);
- Q_ASSERT_SUCCEEDED(hr);
- if (info) {
- hr = info->Cancel();
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
- hr = connectOp.Reset();
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- emit connectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
- return S_OK;
- }
-
- HRESULT OnNewDatagramReceived(IDatagramSocket *, IDatagramSocketMessageReceivedEventArgs *args)
- {
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
- WinRtDatagram datagram;
- QHostAddress returnAddress;
- ComPtr<IHostName> remoteHost;
- HRESULT hr = args->get_RemoteAddress(&remoteHost);
- RETURN_OK_IF_FAILED("Could not obtain remote host");
- HString remoteHostString;
- hr = remoteHost->get_CanonicalName(remoteHostString.GetAddressOf());
- RETURN_OK_IF_FAILED("Could not obtain remote host's canonical name");
- returnAddress.setAddress(qt_QStringFromHString(remoteHostString));
- datagram.header.senderAddress = returnAddress;
- HString remotePort;
- hr = args->get_RemotePort(remotePort.GetAddressOf());
- RETURN_OK_IF_FAILED("Could not obtain remote port");
- datagram.header.senderPort = qt_QStringFromHString(remotePort).toInt();
-
- ComPtr<IDataReader> reader;
- hr = args->GetDataReader(&reader);
- RETURN_OK_IF_FAILED("Could not obtain data reader");
- quint32 length;
- hr = reader->get_UnconsumedBufferLength(&length);
- RETURN_OK_IF_FAILED("Could not obtain unconsumed buffer length");
- datagram.data.resize(length);
- hr = reader->ReadBytes(length, reinterpret_cast<BYTE *>(datagram.data.data()));
- RETURN_OK_IF_FAILED("Could not read datagram");
-
- QMutexLocker locker(&mutex);
- // Notify the engine about new datagrams being present at the next event loop iteration
- if (emitDataReceived)
- emit newDataReceived();
- pendingDatagrams << datagram;
-
- return S_OK;
- }
-
- HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
- {
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
- if (asyncInfo == initialReadOp.Get()) {
- initialReadOp.Reset();
- } else if (asyncInfo == readOp.Get()) {
- readOp.Reset();
- } else {
- Q_ASSERT(false);
- }
-
- // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
- // that the connection was closed. The socket cannot be closed here, as the subsequent read
- // might fail then.
- if (status == Error || status == Canceled) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed";
- emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError);
- return S_OK;
- }
-
- ComPtr<IBuffer> buffer;
- HRESULT hr = asyncInfo->GetResults(&buffer);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to get read results buffer");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
-
- UINT32 bufferLength;
- hr = buffer->get_Length(&bufferLength);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to get buffer length");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
- // A zero sized buffer length signals, that the remote host closed the connection. The socket
- // cannot be closed though, as the following read might have socket descriptor -1 and thus and
- // the closing of the socket won't be communicated to the caller. So only the error is set. The
- // actual socket close happens inside of read.
- if (!bufferLength) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Remote host closed";
- emit socketErrorOccured(QAbstractSocket::RemoteHostClosedError);
- return S_OK;
- }
-
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
- hr = buffer.As(&byteArrayAccess);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to get cast buffer");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
- byte *data;
- hr = byteArrayAccess->Buffer(&data);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to access buffer data");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
-
- QByteArray newData(reinterpret_cast<const char*>(data), qint64(bufferLength));
-
- QMutexLocker readLocker(&mutex);
- emit newDataReceived();
- pendingData.append(newData);
- readLocker.unlock();
-
- hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() {
- UINT32 readBufferLength;
- ComPtr<IInputStream> stream;
- HRESULT hr = tcpSocket->get_InputStream(&stream);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to obtain input stream");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
-
- // Reuse the stream buffer
- hr = buffer->get_Capacity(&readBufferLength);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to get buffer capacity");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
- hr = buffer->put_Length(0);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "Failed to set buffer length");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
-
- hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &readOp);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
- hr = readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketEngineWorker::onReadyRead).Get());
- if (FAILED(hr)) {
- qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
- emit socketErrorOccured(QAbstractSocket::UnknownSocketError);
- return S_OK;
- }
- return S_OK;
- });
- Q_ASSERT_SUCCEEDED(hr);
- return S_OK;
- }
-
- void setTcpSocket(ComPtr<IStreamSocket> socket) { tcpSocket = socket; }
-
-private:
- friend class QNativeSocketEngine;
- ComPtr<IStreamSocket> tcpSocket;
-
- QList<WinRtDatagram> pendingDatagrams;
- bool emitDataReceived = true;
- QByteArray pendingData;
-
- // Protects pendingData/pendingDatagrams which are accessed from native callbacks
- QMutex mutex;
-
- ComPtr<IAsyncAction> connectOp;
- ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> initialReadOp;
- ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp;
-
- QNativeSocketEnginePrivate *enginePrivate;
-};
-
-static QByteArray socketDescription(const QAbstractSocketEngine *s)
-{
- QByteArray result;
- if (const QObject *o = s->parent()) {
- const QString name = o->objectName();
- if (!name.isEmpty()) {
- result += '"';
- result += name.toLocal8Bit();
- result += "\"/";
- }
- result += o->metaObject()->className();
- }
- return result;
-}
-
-// Common constructs
-#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \
- if (!isValid()) { \
- qWarning(""#function" was called on an uninitialized socket device"); \
- return returnValue; \
- } } while (0)
-#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \
- if (isValid()) { \
- qWarning(""#function" was called on an already initialized socket device"); \
- return returnValue; \
- } } while (0)
-#define Q_CHECK_STATE(function, checkState, returnValue) do { \
- if (d->socketState != (checkState)) { \
- qWarning(""#function" was not called in "#checkState); \
- return (returnValue); \
- } } while (0)
-#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \
- if (d->socketState == (checkState)) { \
- qWarning(""#function" was called in "#checkState); \
- return (returnValue); \
- } } while (0)
-#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \
- if (d->socketState != (state1) && d->socketState != (state2)) { \
- qWarning(""#function" was called" \
- " not in "#state1" or "#state2); \
- return (returnValue); \
- } } while (0)
-#define Q_CHECK_STATES3(function, state1, state2, state3, returnValue) do { \
- if (d->socketState != (state1) && d->socketState != (state2) && d->socketState != (state3)) { \
- qWarning(""#function" was called" \
- " not in "#state1", "#state2" or "#state3); \
- return (returnValue); \
- } } while (0)
-#define Q_CHECK_TYPE(function, type, returnValue) do { \
- if (d->socketType != (type)) { \
- qWarning(#function" was called by a" \
- " socket other than "#type""); \
- return (returnValue); \
- } } while (0)
-#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a)
-
-template <typename T>
-static AsyncStatus opStatus(const ComPtr<T> &op)
-{
- ComPtr<IAsyncInfo> info;
- HRESULT hr = op.As(&info);
- Q_ASSERT_SUCCEEDED(hr);
- AsyncStatus status;
- hr = info->get_Status(&status);
- Q_ASSERT_SUCCEEDED(hr);
- return status;
-}
-
-static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
-{
- qCDebug(lcNetworkSocket) << Q_FUNC_INFO << data << len;
- ComPtr<IBuffer> buffer;
- HRESULT hr = g->bufferFactory->Create(len, &buffer);
- Q_ASSERT_SUCCEEDED(hr);
- hr = buffer->put_Length(len);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
- hr = buffer.As(&byteArrayAccess);
- Q_ASSERT_SUCCEEDED(hr);
- byte *bytes;
- hr = byteArrayAccess->Buffer(&bytes);
- Q_ASSERT_SUCCEEDED(hr);
- memcpy(bytes, data, len);
- ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
- hr = stream->WriteAsync(buffer.Get(), &op);
- RETURN_IF_FAILED("Failed to write to stream", return -1);
- UINT32 bytesWritten;
- hr = QWinRTFunctions::await(op, &bytesWritten);
- RETURN_IF_FAILED("Failed to write to stream", return -1);
- return bytesWritten;
-}
-
-QNativeSocketEngine::QNativeSocketEngine(QObject *parent)
- : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << parent;
- qRegisterMetaType<WinRtDatagram>();
- qRegisterMetaType<WinRTSocketEngine::ErrorString>();
- Q_D(QNativeSocketEngine);
-#ifndef QT_NO_SSL
- if (parent)
- d->sslSocket = qobject_cast<QSslSocket *>(parent->parent());
-#endif
-
- connect(this, &QNativeSocketEngine::connectionReady,
- this, &QNativeSocketEngine::connectionNotification, Qt::QueuedConnection);
- connect(this, &QNativeSocketEngine::readReady,
- this, &QNativeSocketEngine::processReadReady, Qt::QueuedConnection);
- connect(this, &QNativeSocketEngine::writeReady,
- this, &QNativeSocketEngine::writeNotification, Qt::QueuedConnection);
- connect(d->worker, &SocketEngineWorker::connectOpFinished,
- this, &QNativeSocketEngine::handleConnectOpFinished, Qt::QueuedConnection);
- connect(d->worker, &SocketEngineWorker::newDataReceived, this, &QNativeSocketEngine::handleNewData, Qt::QueuedConnection);
- connect(d->worker, &SocketEngineWorker::socketErrorOccured,
- this, &QNativeSocketEngine::handleTcpError, Qt::QueuedConnection);
-}
-
-QNativeSocketEngine::~QNativeSocketEngine()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- close();
-}
-
-bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << type << protocol;
- Q_D(QNativeSocketEngine);
- if (isValid())
- close();
-
- // Create the socket
- if (!d->createNewSocket(type, protocol))
- return false;
-
- if (type == QAbstractSocket::UdpSocket) {
- // Set the broadcasting flag if it's a UDP socket.
- if (!setOption(BroadcastSocketOption, 1)) {
- d->setError(QAbstractSocket::UnsupportedSocketOperationError,
- WinRTSocketEngine::BroadcastingInitFailedErrorString);
- close();
- return false;
- }
-
- // Set some extra flags that are interesting to us, but accept failure
- setOption(ReceivePacketInformation, 1);
- setOption(ReceiveHopLimit, 1);
- }
-
-
- // Make sure we receive out-of-band data
- if (type == QAbstractSocket::TcpSocket
- && !setOption(ReceiveOutOfBandData, 1)) {
- qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
- }
-
-
- d->socketType = type;
- d->socketProtocol = protocol;
- return true;
-}
-
-bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketDescriptor << socketState;
- Q_D(QNativeSocketEngine);
-
- if (isValid())
- close();
-
- // Currently, only TCP sockets are initialized this way.
- IStreamSocket *socket = gSocketHandler->pendingTcpSockets.take(socketDescriptor);
- d->socketDescriptor = qintptr(socket);
- d->socketType = QAbstractSocket::TcpSocket;
-
- if (!d->socketDescriptor || !d->fetchConnectionParameters()) {
- d->setError(QAbstractSocket::UnsupportedSocketOperationError,
- WinRTSocketEngine::InvalidSocketErrorString);
- d->socketDescriptor = -1;
- return false;
- }
-
- // Start processing incoming data
- if (d->socketType == QAbstractSocket::TcpSocket) {
- HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, socket, this]() {
- d->worker->setTcpSocket(socket);
- d->worker->startReading();
- return S_OK;
- });
- if (FAILED(hr))
- return false;
- } else {
- d->socketState = socketState;
- }
-
- return true;
-}
-
-qintptr QNativeSocketEngine::socketDescriptor() const
-{
- Q_D(const QNativeSocketEngine);
- return d->socketDescriptor;
-}
-
-bool QNativeSocketEngine::isValid() const
-{
- Q_D(const QNativeSocketEngine);
- return d->socketDescriptor != -1;
-}
-
-bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false);
- Q_CHECK_STATES3(QNativeSocketEngine::connectToHost(), QAbstractSocket::BoundState,
- QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
- const QString addressString = address.toString();
- return connectToHostByName(addressString, port);
-}
-
-bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << name << port;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHostByName(), false);
- Q_CHECK_STATES3(QNativeSocketEngine::connectToHostByName(), QAbstractSocket::BoundState,
- QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
- HRESULT hr;
-
-#if _MSC_VER >= 1900
- ComPtr<IThreadNetworkContext> networkContext;
- if (!qEnvironmentVariableIsEmpty("QT_WINRT_USE_THREAD_NETWORK_CONTEXT")) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Creating network context";
- hr = qt_winrt_try_create_thread_network_context(name, networkContext);
- if (FAILED(hr)) {
- setError(QAbstractSocket::ConnectionRefusedError, QLatin1String("Could not create thread network context."));
- d->socketState = QAbstractSocket::ConnectedState;
- return true;
- }
- }
-#endif // _MSC_VER >= 1900
-
- HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(name.utf16()));
- ComPtr<IHostNameFactory> hostNameFactory;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IHostName> remoteHost;
- hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
- RETURN_FALSE_IF_FAILED("QNativeSocketEngine::connectToHostByName: Could not create hostname.");
-
- const QString portString = QString::number(port);
- HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
- if (d->socketType == QAbstractSocket::TcpSocket)
- hr = d->tcpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp);
- else if (d->socketType == QAbstractSocket::UdpSocket)
- hr = d->udpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->worker->connectOp);
- if (hr == E_ACCESSDENIED) {
- qErrnoWarning(hr, "QNativeSocketEngine::connectToHostByName: Unable to connect to host (%s:%hu/%s). "
- "Please check your manifest capabilities.",
- qPrintable(name), port, socketDescription(this).constData());
- return false;
- }
- Q_ASSERT_SUCCEEDED(hr);
-
-#if _MSC_VER >= 1900
- if (networkContext != nullptr) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "Closing network context";
- ComPtr<IClosable> networkContextCloser;
- hr = networkContext.As(&networkContextCloser);
- Q_ASSERT_SUCCEEDED(hr);
- hr = networkContextCloser->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
-#endif // _MSC_VER >= 1900
-
- d->socketState = QAbstractSocket::ConnectingState;
- QEventDispatcherWinRT::runOnXamlThread([d, &hr]() {
- hr = d->worker->connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
- d->worker, &SocketEngineWorker::onConnectOpFinished).Get());
- RETURN_OK_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
- return S_OK;
- });
- if (FAILED(hr))
- return false;
-
- return d->socketState == QAbstractSocket::ConnectedState;
-}
-
-bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << address << port;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bind(), false);
- Q_CHECK_STATE(QNativeSocketEngine::bind(), QAbstractSocket::UnconnectedState, false);
-
- HRESULT hr;
- // runOnXamlThread may only return S_OK (will assert otherwise) so no need to check its result.
- // hr is set inside the lambda though. If an error occurred hr will point that out.
- bool specificErrorSet = false;
- QEventDispatcherWinRT::runOnXamlThread([address, d, &hr, port, &specificErrorSet, this]() {
- ComPtr<IHostName> hostAddress;
-
- if (address != QHostAddress::Any && address != QHostAddress::AnyIPv4 && address != QHostAddress::AnyIPv6) {
- ComPtr<IHostNameFactory> hostNameFactory;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not obtain hostname factory");
- const QString addressString = address.toString();
- HStringReference addressRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
- hr = hostNameFactory->CreateHostName(addressRef.Get(), &hostAddress);
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create hostname.");
- }
-
- QString portQString = port ? QString::number(port) : QString();
- HStringReference portString(reinterpret_cast<LPCWSTR>(portQString.utf16()));
-
- ComPtr<IAsyncAction> op;
- if (d->socketType == QAbstractSocket::TcpSocket) {
- if (!d->tcpListener) {
- hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(),
- &d->tcpListener);
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not create tcp listener");
- }
-
- hr = d->tcpListener->add_ConnectionReceived(
- Callback<ClientConnectedHandler>(d, &QNativeSocketEnginePrivate::handleClientConnection).Get(),
- &d->connectionToken);
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not register client connection callback");
- hr = d->tcpListener->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
- } else if (d->socketType == QAbstractSocket::UdpSocket) {
- hr = d->udpSocket()->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op);
- }
- if (hr == E_ACCESSDENIED) {
- qErrnoWarning(hr, "Unable to bind socket (%s:%hu/%s). Please check your manifest capabilities.",
- qPrintable(address.toString()), port, socketDescription(this).constData());
- d->setError(QAbstractSocket::SocketAccessError,
- WinRTSocketEngine::AccessErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- specificErrorSet = true;
- return S_OK;
- }
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Unable to bind socket");
-
- hr = QWinRTFunctions::await(op);
- if (hr == 0x80072741) { // The requested address is not valid in its context
- d->setError(QAbstractSocket::SocketAddressNotAvailableError,
- WinRTSocketEngine::AddressNotAvailableErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- specificErrorSet = true;
- return S_OK;
- // Only one usage of each socket address (protocol/network address/port) is normally permitted
- } else if (hr == 0x80072740) {
- d->setError(QAbstractSocket::AddressInUseError,
- WinRTSocketEngine::AddressInuseErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- specificErrorSet = true;
- return S_OK;
- }
- RETURN_OK_IF_FAILED("QNativeSocketEngine::bind: Could not wait for bind to finish");
- return S_OK;
- });
- if (FAILED(hr)) {
- if (!specificErrorSet) {
- d->setError(QAbstractSocket::UnknownSocketError,
- WinRTSocketEngine::UnknownSocketErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- }
- return false;
- }
-
- d->socketState = QAbstractSocket::BoundState;
- return d->fetchConnectionParameters();
-}
-
-bool QNativeSocketEngine::listen()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
- Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
-#if QT_CONFIG(sctp)
- Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket,
- QAbstractSocket::SctpSocket, false);
-#else
- Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
-#endif
-
- if (d->tcpListener && d->socketDescriptor != -1) {
- d->socketState = QAbstractSocket::ListeningState;
- return true;
- }
- return false;
-}
-
-int QNativeSocketEngine::accept()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
- Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1);
-#if QT_CONFIG(sctp)
- Q_CHECK_TYPES(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket,
- QAbstractSocket::SctpSocket, -1);
-#else
- Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1);
-#endif
-
- if (d->socketDescriptor == -1 || d->pendingConnections.isEmpty()) {
- d->setError(QAbstractSocket::TemporaryError, WinRTSocketEngine::TemporaryErrorString);
- return -1;
- }
-
- if (d->socketType == QAbstractSocket::TcpSocket) {
- IStreamSocket *socket = d->pendingConnections.takeFirst();
-
- SocketHandler *handler = gSocketHandler();
- handler->pendingTcpSockets.insert(++handler->socketCount, socket);
- return handler->socketCount;
- }
-
- return -1;
-}
-
-void QNativeSocketEngine::close()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- Q_D(QNativeSocketEngine);
-
- if (d->closingDown)
- return;
-
- if (d->pendingReadNotification) {
- // We use QPointer here to see if this QNativeSocketEngine was deleted as a result of
- // finishing and cleaning up a network request when calling "processReadReady".
- QPointer<QNativeSocketEngine> alive(this);
- processReadReady();
- if (alive.isNull())
- return;
- }
-
- d->closingDown = true;
-
- d->notifyOnRead = false;
- d->notifyOnWrite = false;
- d->notifyOnException = false;
- d->emitReadReady = false;
-
- HRESULT hr;
- if (d->socketType == QAbstractSocket::TcpSocket) {
- hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
- HRESULT hr;
- // To close the connection properly (not with a hard reset) all pending read operation have to
- // be finished or cancelled. The API isn't available on Windows 8.1 though.
- ComPtr<IStreamSocket3> socket3;
- hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket3));
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IAsyncAction> action;
- hr = socket3->CancelIOAsync(&action);
- Q_ASSERT_SUCCEEDED(hr);
- hr = QWinRTFunctions::await(action, QWinRTFunctions::YieldThread, 5000);
- // If there is no pending IO (no read established before) the function will fail with
- // "function was called at an unexpected time" which is fine.
- // Timeout is fine as well. The result will be the socket being hard reset instead of
- // being closed gracefully
- if (hr != E_ILLEGAL_METHOD_CALL && hr != ERROR_TIMEOUT)
- Q_ASSERT_SUCCEEDED(hr);
- return S_OK;
- });
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- if (d->socketDescriptor != -1) {
- ComPtr<IClosable> socket;
- if (d->socketType == QAbstractSocket::TcpSocket) {
- hr = d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket));
- Q_ASSERT_SUCCEEDED(hr);
- hr = d->tcpSocket()->Release();
- Q_ASSERT_SUCCEEDED(hr);
- } else if (d->socketType == QAbstractSocket::UdpSocket) {
- hr = d->udpSocket()->QueryInterface(IID_PPV_ARGS(&socket));
- Q_ASSERT_SUCCEEDED(hr);
- hr = d->udpSocket()->Release();
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- if (socket) {
- hr = socket->Close();
- Q_ASSERT_SUCCEEDED(hr);
- }
- d->socketDescriptor = -1;
- }
- d->socketState = QAbstractSocket::UnconnectedState;
- d->hasSetSocketError = false;
- d->localPort = 0;
- d->localAddress.clear();
- d->peerPort = 0;
- d->peerAddress.clear();
- d->inboundStreamCount = d->outboundStreamCount = 0;
-}
-
-bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
-{
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false);
- Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
- Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface;
- Q_UNIMPLEMENTED();
- return false;
-}
-
-bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)
-{
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false);
- Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false);
- Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false);
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << groupAddress << iface;
- Q_UNIMPLEMENTED();
- return false;
-}
-
-QNetworkInterface QNativeSocketEngine::multicastInterface() const
-{
- Q_D(const QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface());
- Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface());
- Q_UNIMPLEMENTED();
- return QNetworkInterface();
-}
-
-bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
-{
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false);
- Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false);
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << iface;
- Q_UNIMPLEMENTED();
- return false;
-}
-
-qint64 QNativeSocketEngine::bytesAvailable() const
-{
- Q_D(const QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bytesAvailable(), -1);
- Q_CHECK_NOT_STATE(QNativeSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, -1);
- if (d->socketType != QAbstractSocket::TcpSocket)
- return -1;
-
- QMutexLocker locker(&d->worker->mutex);
- const qint64 bytesAvailable = d->worker->pendingData.length();
-
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << bytesAvailable;
- return bytesAvailable;
-}
-
-qint64 QNativeSocketEngine::read(char *data, qint64 maxlen)
-{
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << maxlen;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1);
- Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);
- if (d->socketType != QAbstractSocket::TcpSocket)
- return -1;
-
- // There will be a read notification when the socket was closed by the remote host. If that
- // happens and there isn't anything left in the buffer, we have to return -1 in order to signal
- // the closing of the socket.
- QMutexLocker mutexLocker(&d->worker->mutex);
- if (d->worker->pendingData.isEmpty() && d->socketState != QAbstractSocket::ConnectedState) {
- close();
- return -1;
- }
-
- QByteArray readData;
- const int copyLength = qMin(maxlen, qint64(d->worker->pendingData.length()));
- if (maxlen >= d->worker->pendingData.length()) {
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading full buffer";
- readData = d->worker->pendingData;
- d->worker->pendingData.clear();
- d->emitReadReady = true;
- } else {
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Reading part of the buffer ("
- << copyLength << "of" << d->worker->pendingData.length() << "bytes";
- readData = d->worker->pendingData.left(maxlen);
- d->worker->pendingData.remove(0, maxlen);
- if (d->notifyOnRead) {
- d->pendingReadNotification = true;
- emit readReady();
- }
- }
- mutexLocker.unlock();
-
- memcpy(data, readData, copyLength);
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Read" << copyLength << "bytes";
- return copyLength;
-}
-
-qint64 QNativeSocketEngine::write(const char *data, qint64 len)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1);
- Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1);
-
- HRESULT hr = E_FAIL;
- ComPtr<IOutputStream> stream;
- if (d->socketType == QAbstractSocket::TcpSocket)
- hr = d->tcpSocket()->get_OutputStream(&stream);
- else if (d->socketType == QAbstractSocket::UdpSocket)
- hr = d->udpSocket()->get_OutputStream(&stream);
- Q_ASSERT_SUCCEEDED(hr);
-
- qint64 bytesWritten = writeIOStream(stream, data, len);
- if (bytesWritten < 0)
- d->setError(QAbstractSocket::SocketAccessError, WinRTSocketEngine::AccessErrorString);
- else if (bytesWritten > 0 && d->notifyOnWrite)
- emit writeReady();
-
- return bytesWritten;
-}
-
-qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header,
- PacketHeaderOptions)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << maxlen;
-#ifndef QT_NO_UDPSOCKET
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1);
- Q_CHECK_STATES(QNativeSocketEngine::readDatagram(), QAbstractSocket::BoundState,
- QAbstractSocket::ConnectedState, -1);
-
- QMutexLocker locker(&d->worker->mutex);
- if (d->socketType != QAbstractSocket::UdpSocket || d->worker->pendingDatagrams.isEmpty()) {
- if (header)
- header->clear();
- return -1;
- }
-
- WinRtDatagram datagram = d->worker->pendingDatagrams.takeFirst();
- if (header)
- *header = datagram.header;
-
- QByteArray readOrigin;
- if (maxlen < datagram.data.length())
- readOrigin = datagram.data.left(maxlen);
- else
- readOrigin = datagram.data;
- if (d->worker->pendingDatagrams.isEmpty()) {
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << "That's all folks";
- d->worker->emitDataReceived = true;
- d->emitReadReady = true;
- }
-
- locker.unlock();
- memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length())));
- return readOrigin.length();
-#else
- Q_UNUSED(data)
- Q_UNUSED(maxlen)
- Q_UNUSED(header)
- return -1;
-#endif // QT_NO_UDPSOCKET
-}
-
-qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << data << len;
-#ifndef QT_NO_UDPSOCKET
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1);
- Q_CHECK_STATES(QNativeSocketEngine::writeDatagram(), QAbstractSocket::BoundState,
- QAbstractSocket::ConnectedState, -1);
-
- if (d->socketType != QAbstractSocket::UdpSocket)
- return -1;
-
- ComPtr<IHostName> remoteHost;
- ComPtr<IHostNameFactory> hostNameFactory;
-
- HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- Q_ASSERT_SUCCEEDED(hr);
- const QString addressString = header.destinationAddress.toString();
- HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
- hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
- RETURN_IF_FAILED("QNativeSocketEngine::writeDatagram: Could not create hostname.", return -1);
-
- ComPtr<IAsyncOperation<IOutputStream *>> streamOperation;
- ComPtr<IOutputStream> stream;
- const QString portString = QString::number(header.destinationPort);
- HStringReference portRef(reinterpret_cast<LPCWSTR>(portString.utf16()));
- hr = d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = QWinRTFunctions::await(streamOperation, stream.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
-
- return writeIOStream(stream, data, len);
-#else
- Q_UNUSED(data)
- Q_UNUSED(len)
- Q_UNUSED(header)
- return -1;
-#endif // QT_NO_UDPSOCKET
-}
-
-bool QNativeSocketEngine::hasPendingDatagrams() const
-{
- Q_D(const QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::hasPendingDatagrams(), false);
- Q_CHECK_NOT_STATE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false);
- Q_CHECK_TYPE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false);
-
- QMutexLocker locker(&d->worker->mutex);
- return d->worker->pendingDatagrams.length() > 0;
-}
-
-qint64 QNativeSocketEngine::pendingDatagramSize() const
-{
- Q_D(const QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::pendingDatagramSize(), -1);
- Q_CHECK_TYPE(QNativeSocketEngine::pendingDatagramSize(), QAbstractSocket::UdpSocket, -1);
-
- QMutexLocker locker(&d->worker->mutex);
- if (d->worker->pendingDatagrams.isEmpty())
- return -1;
-
- return d->worker->pendingDatagrams.at(0).data.length();
-}
-
-qint64 QNativeSocketEngine::bytesToWrite() const
-{
- return 0;
-}
-
-qint64 QNativeSocketEngine::receiveBufferSize() const
-{
- Q_D(const QNativeSocketEngine);
- return d->option(QAbstractSocketEngine::ReceiveBufferSocketOption);
-}
-
-void QNativeSocketEngine::setReceiveBufferSize(qint64 bufferSize)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize;
- Q_D(QNativeSocketEngine);
- d->setOption(QAbstractSocketEngine::ReceiveBufferSocketOption, bufferSize);
-}
-
-qint64 QNativeSocketEngine::sendBufferSize() const
-{
- Q_D(const QNativeSocketEngine);
- return d->option(QAbstractSocketEngine::SendBufferSocketOption);
-}
-
-void QNativeSocketEngine::setSendBufferSize(qint64 bufferSize)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << bufferSize;
- Q_D(QNativeSocketEngine);
- d->setOption(QAbstractSocketEngine::SendBufferSocketOption, bufferSize);
-}
-
-int QNativeSocketEngine::option(QAbstractSocketEngine::SocketOption option) const
-{
- Q_D(const QNativeSocketEngine);
- return d->option(option);
-}
-
-bool QNativeSocketEngine::setOption(QAbstractSocketEngine::SocketOption option, int value)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << option << value;
- Q_D(QNativeSocketEngine);
- return d->setOption(option, value);
-}
-
-bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
- Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForRead(),
- QAbstractSocket::UnconnectedState, false);
-
- if (timedOut)
- *timedOut = false;
-
- QElapsedTimer timer;
- timer.start();
- while (msecs > timer.elapsed()) {
- // Servers with active connections are ready for reading
- if (!d->currentConnections.isEmpty())
- return true;
-
- // If we are a client, we are ready to read if our buffer has data
- QMutexLocker locker(&d->worker->mutex);
- if (!d->worker->pendingData.isEmpty())
- return true;
-
- // Nothing to do, wait for more events
- d->eventLoop.processEvents();
- }
-
- d->setError(QAbstractSocket::SocketTimeoutError,
- WinRTSocketEngine::TimeOutErrorString);
-
- if (timedOut)
- *timedOut = true;
- return false;
-}
-
-bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << msecs;
- Q_UNUSED(timedOut);
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
- Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(),
- QAbstractSocket::UnconnectedState, false);
-
- if (d->socketState == QAbstractSocket::ConnectingState) {
- HRESULT hr = QWinRTFunctions::await(d->worker->connectOp, QWinRTFunctions::ProcessMainThreadEvents);
- if (SUCCEEDED(hr)) {
- handleConnectOpFinished(true, QAbstractSocket::UnknownSocketError, WinRTSocketEngine::UnknownSocketErrorString);
- return true;
- }
- }
- return false;
-}
-
-bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite, bool checkRead, bool checkWrite, int msecs, bool *timedOut)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << checkRead << checkWrite << msecs;
- Q_D(QNativeSocketEngine);
- Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false);
- Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
- QAbstractSocket::UnconnectedState, false);
-
- Q_UNUSED(readyToRead);
- Q_UNUSED(readyToWrite);
- Q_UNUSED(timedOut);
- return false;
-}
-
-bool QNativeSocketEngine::isReadNotificationEnabled() const
-{
- Q_D(const QNativeSocketEngine);
- return d->notifyOnRead;
-}
-
-void QNativeSocketEngine::setReadNotificationEnabled(bool enable)
-{
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
- Q_D(QNativeSocketEngine);
- d->notifyOnRead = enable;
-}
-
-bool QNativeSocketEngine::isWriteNotificationEnabled() const
-{
- Q_D(const QNativeSocketEngine);
- return d->notifyOnWrite;
-}
-
-void QNativeSocketEngine::setWriteNotificationEnabled(bool enable)
-{
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
- Q_D(QNativeSocketEngine);
- d->notifyOnWrite = enable;
- if (enable && d->socketState == QAbstractSocket::ConnectedState) {
- if (bytesToWrite())
- return; // will be emitted as a result of bytes written
- writeNotification();
- }
-}
-
-bool QNativeSocketEngine::isExceptionNotificationEnabled() const
-{
- Q_D(const QNativeSocketEngine);
- return d->notifyOnException;
-}
-
-void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable)
-{
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << enable;
- Q_D(QNativeSocketEngine);
- d->notifyOnException = enable;
-}
-
-void QNativeSocketEngine::establishRead()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- Q_D(QNativeSocketEngine);
-
- HRESULT hr;
- hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
- d->worker->setTcpSocket(d->tcpSocket());
- d->worker->startReading();
- return S_OK;
- });
- Q_ASSERT_SUCCEEDED(hr);
-}
-
-void QNativeSocketEngine::handleConnectOpFinished(bool success, QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << success << error << errorString;
- Q_D(QNativeSocketEngine);
- disconnect(d->worker, &SocketEngineWorker::connectOpFinished,
- this, &QNativeSocketEngine::handleConnectOpFinished);
- if (!success) {
- d->setError(error, errorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- close();
- return;
- }
-
- d->socketState = QAbstractSocket::ConnectedState;
- d->fetchConnectionParameters();
- emit connectionReady();
-
- if (d->socketType != QAbstractSocket::TcpSocket)
- return;
-
-#ifndef QT_NO_SSL
- // Delay the reader so that the SSL socket can upgrade
- if (d->sslSocket)
- QObject::connect(qobject_cast<QSslSocket *>(d->sslSocket), &QSslSocket::encrypted, this, &QNativeSocketEngine::establishRead);
- else
-#endif
- establishRead();
-}
-
-void QNativeSocketEngine::handleNewData()
-{
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO;
- Q_D(QNativeSocketEngine);
-
- if (d->notifyOnRead && d->emitReadReady) {
- if (d->socketType == QAbstractSocket::UdpSocket && !d->worker->emitDataReceived)
- return;
- qCDebug(lcNetworkSocketVerbose) << this << Q_FUNC_INFO << "Emitting readReady";
- d->pendingReadNotification = true;
- emit readReady();
- d->worker->emitDataReceived = false;
- d->emitReadReady = false;
- }
-}
-
-void QNativeSocketEngine::handleTcpError(QAbstractSocket::SocketError error)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error;
- Q_D(QNativeSocketEngine);
- WinRTSocketEngine::ErrorString errorString;
- switch (error) {
- case QAbstractSocket::RemoteHostClosedError:
- errorString = WinRTSocketEngine::RemoteHostClosedErrorString;
- break;
- default:
- errorString = WinRTSocketEngine::UnknownSocketErrorString;
- }
-
- d->setError(error, errorString);
- close();
-}
-
-void QNativeSocketEngine::processReadReady()
-{
- Q_D(QNativeSocketEngine);
- if (d->closingDown)
- return;
-
- d->pendingReadNotification = false;
- readNotification();
-}
-
-bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << socketType << socketProtocol;
- Q_UNUSED(socketProtocol);
- HRESULT hr;
-
- switch (socketType) {
- case QAbstractSocket::TcpSocket: {
- ComPtr<IStreamSocket> socket;
- hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &socket);
- RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance");
- socketDescriptor = qintptr(socket.Detach());
- break;
- }
- case QAbstractSocket::UdpSocket: {
- ComPtr<IDatagramSocket> socket;
- hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &socket);
- RETURN_FALSE_IF_FAILED("createNewSocket: Could not create socket instance");
- socketDescriptor = qintptr(socket.Detach());
- QEventDispatcherWinRT::runOnXamlThread([&hr, this]() {
- hr = udpSocket()->add_MessageReceived(Callback<DatagramReceivedHandler>(worker, &SocketEngineWorker::OnNewDatagramReceived).Get(), &connectionToken);
- if (FAILED(hr)) {
- qErrnoWarning(hr, "createNewSocket: Could not add \"message received\" callback");
- return hr;
- }
- return S_OK;
- });
- if (FAILED(hr))
- return false;
- break;
- }
- default:
- qWarning("Invalid socket type");
- return false;
- }
-
- this->socketType = socketType;
-
- // Make the socket nonblocking.
- if (!setOption(QAbstractSocketEngine::NonBlockingSocketOption, 1)) {
- setError(QAbstractSocket::UnsupportedSocketOperationError, WinRTSocketEngine::NonBlockingInitFailedErrorString);
- q_func()->close();
- return false;
- }
-
- return true;
-}
-
-QNativeSocketEnginePrivate::QNativeSocketEnginePrivate()
- : QAbstractSocketEnginePrivate()
- , notifyOnRead(true)
- , notifyOnWrite(true)
- , notifyOnException(false)
- , closingDown(false)
- , socketDescriptor(-1)
- , worker(new SocketEngineWorker(this))
- , sslSocket(nullptr)
- , connectionToken( { -1 } )
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
-}
-
-QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- if (socketDescriptor == -1 || connectionToken.value == -1)
- return;
-
- HRESULT hr;
- if (socketType == QAbstractSocket::UdpSocket)
- hr = udpSocket()->remove_MessageReceived(connectionToken);
- else if (socketType == QAbstractSocket::TcpSocket)
- hr = tcpListener->remove_ConnectionReceived(connectionToken);
- Q_ASSERT_SUCCEEDED(hr);
-
- worker->deleteLater();
-}
-
-void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString) const
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << error << errorString;
- if (hasSetSocketError) {
- // Only set socket errors once for one engine; expect the
- // socket to recreate its engine after an error. Note: There's
- // one exception: SocketError(11) bypasses this as it's purely
- // a temporary internal error condition.
- // Another exception is the way the waitFor*() functions set
- // an error when a timeout occurs. After the call to setError()
- // they reset the hasSetSocketError to false
- return;
- }
- if (error != QAbstractSocket::SocketError(11))
- hasSetSocketError = true;
-
- socketError = error;
-
- switch (errorString) {
- case WinRTSocketEngine::NonBlockingInitFailedErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unable to initialize non-blocking socket");
- break;
- case WinRTSocketEngine::BroadcastingInitFailedErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket");
- break;
- // should not happen anymore
- case WinRTSocketEngine::NoIpV6ErrorString:
- socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
- break;
- case WinRTSocketEngine::RemoteHostClosedErrorString:
- socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection");
- break;
- case WinRTSocketEngine::TimeOutErrorString:
- socketErrorString = QNativeSocketEngine::tr("Network operation timed out");
- break;
- case WinRTSocketEngine::ResourceErrorString:
- socketErrorString = QNativeSocketEngine::tr("Out of resources");
- break;
- case WinRTSocketEngine::OperationUnsupportedErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation");
- break;
- case WinRTSocketEngine::ProtocolUnsupportedErrorString:
- socketErrorString = QNativeSocketEngine::tr("Protocol type not supported");
- break;
- case WinRTSocketEngine::InvalidSocketErrorString:
- socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor");
- break;
- case WinRTSocketEngine::HostUnreachableErrorString:
- socketErrorString = QNativeSocketEngine::tr("Host unreachable");
- break;
- case WinRTSocketEngine::NetworkUnreachableErrorString:
- socketErrorString = QNativeSocketEngine::tr("Network unreachable");
- break;
- case WinRTSocketEngine::AccessErrorString:
- socketErrorString = QNativeSocketEngine::tr("Permission denied");
- break;
- case WinRTSocketEngine::ConnectionTimeOutErrorString:
- socketErrorString = QNativeSocketEngine::tr("Connection timed out");
- break;
- case WinRTSocketEngine::ConnectionRefusedErrorString:
- socketErrorString = QNativeSocketEngine::tr("Connection refused");
- break;
- case WinRTSocketEngine::AddressInuseErrorString:
- socketErrorString = QNativeSocketEngine::tr("The bound address is already in use");
- break;
- case WinRTSocketEngine::AddressNotAvailableErrorString:
- socketErrorString = QNativeSocketEngine::tr("The address is not available");
- break;
- case WinRTSocketEngine::AddressProtectedErrorString:
- socketErrorString = QNativeSocketEngine::tr("The address is protected");
- break;
- case WinRTSocketEngine::DatagramTooLargeErrorString:
- socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send");
- break;
- case WinRTSocketEngine::SendDatagramErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unable to send a message");
- break;
- case WinRTSocketEngine::ReceiveDatagramErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unable to receive a message");
- break;
- case WinRTSocketEngine::WriteErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unable to write");
- break;
- case WinRTSocketEngine::ReadErrorString:
- socketErrorString = QNativeSocketEngine::tr("Network error");
- break;
- case WinRTSocketEngine::PortInuseErrorString:
- socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port");
- break;
- case WinRTSocketEngine::NotSocketErrorString:
- socketErrorString = QNativeSocketEngine::tr("Operation on non-socket");
- break;
- case WinRTSocketEngine::InvalidProxyTypeString:
- socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation");
- break;
- case WinRTSocketEngine::TemporaryErrorString:
- socketErrorString = QNativeSocketEngine::tr("Temporary error");
- break;
- case WinRTSocketEngine::UnknownSocketErrorString:
- socketErrorString = QNativeSocketEngine::tr("Unknown error");
- break;
- }
-}
-
-int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) const
-{
- ComPtr<IStreamSocketControl> control;
- if (socketType == QAbstractSocket::TcpSocket) {
- if (FAILED(tcpSocket()->get_Control(&control))) {
- qWarning("QNativeSocketEnginePrivate::option: Could not obtain socket control");
- return -1;
- }
- }
- switch (opt) {
- case QAbstractSocketEngine::NonBlockingSocketOption:
- case QAbstractSocketEngine::BroadcastSocketOption:
- case QAbstractSocketEngine::ReceiveOutOfBandData:
- return 1;
- case QAbstractSocketEngine::SendBufferSocketOption:
- if (socketType == QAbstractSocket::UdpSocket)
- return -1;
-
- UINT32 bufferSize;
- if (FAILED(control->get_OutboundBufferSizeInBytes(&bufferSize))) {
- qWarning("Could not obtain OutboundBufferSizeInBytes information vom socket control");
- return -1;
- }
- return bufferSize;
- case QAbstractSocketEngine::LowDelayOption:
- if (socketType == QAbstractSocket::UdpSocket)
- return -1;
-
- boolean noDelay;
- if (FAILED(control->get_NoDelay(&noDelay))) {
- qWarning("Could not obtain NoDelay information from socket control");
- return -1;
- }
- return noDelay;
- case QAbstractSocketEngine::KeepAliveOption:
- if (socketType == QAbstractSocket::UdpSocket)
- return -1;
-
- boolean keepAlive;
- if (FAILED(control->get_KeepAlive(&keepAlive))) {
- qWarning("Could not obtain KeepAlive information from socket control");
- return -1;
- }
- return keepAlive;
- case QAbstractSocketEngine::ReceiveBufferSocketOption:
- case QAbstractSocketEngine::AddressReusable:
- case QAbstractSocketEngine::BindExclusively:
- case QAbstractSocketEngine::MulticastTtlOption:
- case QAbstractSocketEngine::MulticastLoopbackOption:
- case QAbstractSocketEngine::TypeOfServiceOption:
- case QAbstractSocketEngine::MaxStreamsSocketOption:
- case QAbstractSocketEngine::PathMtuInformation:
- default:
- return -1;
- }
- return -1;
-}
-
-bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption opt, int v)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO << opt << v;
- ComPtr<IStreamSocketControl> control;
- if (socketType == QAbstractSocket::TcpSocket) {
- if (FAILED(tcpSocket()->get_Control(&control))) {
- qWarning("QNativeSocketEnginePrivate::setOption: Could not obtain socket control");
- return false;
- }
- }
- switch (opt) {
- case QAbstractSocketEngine::NonBlockingSocketOption:
- case QAbstractSocketEngine::BroadcastSocketOption:
- case QAbstractSocketEngine::ReceiveOutOfBandData:
- return v != 0;
- case QAbstractSocketEngine::SendBufferSocketOption:
- if (socketType == QAbstractSocket::UdpSocket)
- return false;
-
- if (FAILED(control->put_OutboundBufferSizeInBytes(v))) {
- qWarning("Could not set OutboundBufferSizeInBytes");
- return false;
- }
- return true;
- case QAbstractSocketEngine::LowDelayOption: {
- if (socketType == QAbstractSocket::UdpSocket)
- return false;
-
- boolean noDelay = v;
- if (FAILED(control->put_NoDelay(noDelay))) {
- qWarning("Could not obtain NoDelay information from socket control");
- return false;
- }
- return true;
- }
- case QAbstractSocketEngine::KeepAliveOption: {
- if (socketType == QAbstractSocket::UdpSocket
- || socketState != QAbstractSocket::UnconnectedState)
- return false;
-
- boolean keepAlive = v;
- if (FAILED(control->put_KeepAlive(keepAlive))) {
- qWarning("Could not set KeepAlive value");
- return false;
- }
- return true;
- }
- case QAbstractSocketEngine::ReceiveBufferSocketOption:
- case QAbstractSocketEngine::AddressReusable:
- case QAbstractSocketEngine::BindExclusively:
- case QAbstractSocketEngine::MulticastTtlOption:
- case QAbstractSocketEngine::MulticastLoopbackOption:
- case QAbstractSocketEngine::TypeOfServiceOption:
- case QAbstractSocketEngine::MaxStreamsSocketOption:
- case QAbstractSocketEngine::PathMtuInformation:
- default:
- return false;
- }
- return false;
-}
-
-bool QNativeSocketEnginePrivate::fetchConnectionParameters()
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- localPort = 0;
- localAddress.clear();
- peerPort = 0;
- peerAddress.clear();
- inboundStreamCount = outboundStreamCount = 0;
-
- HRESULT hr;
- if (socketType == QAbstractSocket::TcpSocket) {
- ComPtr<IHostName> hostName;
- HString tmpHString;
- ComPtr<IStreamSocketInformation> info;
- hr = tcpSocket()->get_Information(&info);
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->get_LocalAddress(&hostName);
- Q_ASSERT_SUCCEEDED(hr);
- if (hostName) {
- hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- localAddress.setAddress(qt_QStringFromHString(tmpHString));
- hr = info->get_LocalPort(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- localPort = qt_QStringFromHString(tmpHString).toInt();
- }
- if (!localPort && tcpListener) {
- ComPtr<IStreamSocketListenerInformation> listenerInfo = 0;
- hr = tcpListener->get_Information(&listenerInfo);
- Q_ASSERT_SUCCEEDED(hr);
- hr = listenerInfo->get_LocalPort(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- localPort = qt_QStringFromHString(tmpHString).toInt();
- localAddress = QHostAddress::Any;
- }
- info->get_RemoteAddress(&hostName);
- if (hostName) {
- hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- peerAddress.setAddress(qt_QStringFromHString(tmpHString));
- hr = info->get_RemotePort(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- peerPort = qt_QStringFromHString(tmpHString).toInt();
- inboundStreamCount = outboundStreamCount = 1;
- }
- } else if (socketType == QAbstractSocket::UdpSocket) {
- ComPtr<IHostName> hostName;
- HString tmpHString;
- ComPtr<IDatagramSocketInformation> info;
- hr = udpSocket()->get_Information(&info);
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->get_LocalAddress(&hostName);
- Q_ASSERT_SUCCEEDED(hr);
- if (hostName) {
- hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- localAddress.setAddress(qt_QStringFromHString(tmpHString));
- hr = info->get_LocalPort(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- localPort = qt_QStringFromHString(tmpHString).toInt();
- }
-
- hr = info->get_RemoteAddress(&hostName);
- Q_ASSERT_SUCCEEDED(hr);
- if (hostName) {
- hr = hostName->get_CanonicalName(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- peerAddress.setAddress(qt_QStringFromHString(tmpHString));
- hr = info->get_RemotePort(tmpHString.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- peerPort = qt_QStringFromHString(tmpHString).toInt();
- inboundStreamCount = outboundStreamCount = 1;
- }
- }
- return true;
-}
-
-HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener *listener, IStreamSocketListenerConnectionReceivedEventArgs *args)
-{
- qCDebug(lcNetworkSocket) << this << Q_FUNC_INFO;
- Q_Q(QNativeSocketEngine);
- Q_UNUSED(listener)
- IStreamSocket *socket;
- args->get_Socket(&socket);
- pendingConnections.append(socket);
- emit q->connectionReady();
- if (notifyOnRead)
- emit q->readReady();
- return S_OK;
-}
-
-QT_END_NAMESPACE
-
-#include "qnativesocketengine_winrt.moc"
diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h
deleted file mode 100644
index e1fe58bb97..0000000000
--- a/src/network/socket/qnativesocketengine_winrt_p.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QNATIVESOCKETENGINE_WINRT_P_H
-#define QNATIVESOCKETENGINE_WINRT_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/QEventLoop>
-#include <QtCore/QBuffer>
-#include <QtCore/QLoggingCategory>
-#include <QtCore/QMutex>
-#include <QtCore/QAtomicInteger>
-#include "QtNetwork/qhostaddress.h"
-#include "private/qabstractsocketengine_p.h"
-#include <wrl.h>
-#include <windows.networking.sockets.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_LOGGING_CATEGORY(lcNetworkSocket)
-Q_DECLARE_LOGGING_CATEGORY(lcNetworkSocketVerbose)
-
-namespace WinRTSocketEngine {
- enum ErrorString {
- NonBlockingInitFailedErrorString,
- BroadcastingInitFailedErrorString,
- NoIpV6ErrorString,
- RemoteHostClosedErrorString,
- TimeOutErrorString,
- ResourceErrorString,
- OperationUnsupportedErrorString,
- ProtocolUnsupportedErrorString,
- InvalidSocketErrorString,
- HostUnreachableErrorString,
- NetworkUnreachableErrorString,
- AccessErrorString,
- ConnectionTimeOutErrorString,
- ConnectionRefusedErrorString,
- AddressInuseErrorString,
- AddressNotAvailableErrorString,
- AddressProtectedErrorString,
- DatagramTooLargeErrorString,
- SendDatagramErrorString,
- ReceiveDatagramErrorString,
- WriteErrorString,
- ReadErrorString,
- PortInuseErrorString,
- NotSocketErrorString,
- InvalidProxyTypeString,
- TemporaryErrorString,
-
- UnknownSocketErrorString = -1
- };
-}
-
-class QNativeSocketEnginePrivate;
-class SocketEngineWorker;
-
-struct WinRtDatagram {
- QByteArray data;
- QIpPacketHeader header;
-};
-
-class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
-{
- Q_OBJECT
-public:
- QNativeSocketEngine(QObject *parent = 0);
- ~QNativeSocketEngine();
-
- bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
- bool initialize(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
-
- qintptr socketDescriptor() const;
-
- bool isValid() const;
-
- bool connectToHost(const QHostAddress &address, quint16 port);
- bool connectToHostByName(const QString &name, quint16 port);
- bool bind(const QHostAddress &address, quint16 port);
- bool listen();
- int accept();
- void close();
-
-#ifndef QT_NO_NETWORKINTERFACE
- bool joinMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- bool leaveMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- QNetworkInterface multicastInterface() const;
- bool setMulticastInterface(const QNetworkInterface &iface);
-#endif
-
- qint64 bytesAvailable() const;
-
- qint64 read(char *data, qint64 maxlen);
- qint64 write(const char *data, qint64 len);
-
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0, PacketHeaderOptions = WantNone);
- qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header);
- bool hasPendingDatagrams() const;
- qint64 pendingDatagramSize() const;
-
- qint64 bytesToWrite() const;
-
- qint64 receiveBufferSize() const;
- void setReceiveBufferSize(qint64 bufferSize);
-
- qint64 sendBufferSize() const;
- void setSendBufferSize(qint64 bufferSize);
-
- int option(SocketOption option) const;
- bool setOption(SocketOption option, int value);
-
- bool waitForRead(int msecs = 30000, bool *timedOut = 0);
- bool waitForWrite(int msecs = 30000, bool *timedOut = 0);
- bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
- bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = 0);
-
- bool isReadNotificationEnabled() const;
- void setReadNotificationEnabled(bool enable);
- bool isWriteNotificationEnabled() const;
- void setWriteNotificationEnabled(bool enable);
- bool isExceptionNotificationEnabled() const;
- void setExceptionNotificationEnabled(bool enable);
-
-signals:
- void connectionReady();
- void readReady();
- void writeReady();
- void newDatagramReceived(const WinRtDatagram &datagram);
-
-private slots:
- void establishRead();
- void handleConnectOpFinished(bool success, QAbstractSocket::SocketError error,
- WinRTSocketEngine::ErrorString errorString);
- void handleNewData();
- void handleTcpError(QAbstractSocket::SocketError error);
- void processReadReady();
-
-private:
- Q_DECLARE_PRIVATE(QNativeSocketEngine)
- Q_DISABLE_COPY_MOVE(QNativeSocketEngine)
-};
-
-class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
-{
- Q_DECLARE_PUBLIC(QNativeSocketEngine)
-public:
- QNativeSocketEnginePrivate();
- ~QNativeSocketEnginePrivate();
-
- qintptr socketDescriptor;
- SocketEngineWorker *worker;
-
- bool notifyOnRead, notifyOnWrite, notifyOnException;
- QAtomicInt closingDown;
-
- void setError(QAbstractSocket::SocketError error, WinRTSocketEngine::ErrorString errorString) const;
-
- // native functions
- int option(QNativeSocketEngine::SocketOption option) const;
- bool setOption(QNativeSocketEngine::SocketOption option, int value);
-
- bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
-
- bool checkProxy(const QHostAddress &address);
- bool fetchConnectionParameters();
-
-private:
- inline ABI::Windows::Networking::Sockets::IStreamSocket *tcpSocket() const
- { return reinterpret_cast<ABI::Windows::Networking::Sockets::IStreamSocket *>(socketDescriptor); }
- inline ABI::Windows::Networking::Sockets::IDatagramSocket *udpSocket() const
- { return reinterpret_cast<ABI::Windows::Networking::Sockets::IDatagramSocket *>(socketDescriptor); }
- Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> tcpListener;
-
- QList<ABI::Windows::Networking::Sockets::IStreamSocket *> pendingConnections;
- QList<ABI::Windows::Networking::Sockets::IStreamSocket *> currentConnections;
- QEventLoop eventLoop;
- QAbstractSocket *sslSocket;
- EventRegistrationToken connectionToken;
-
- bool emitReadReady = true;
- bool pendingReadNotification = false;
-
- HRESULT handleClientConnection(ABI::Windows::Networking::Sockets::IStreamSocketListener *tcpListener,
- ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args);
-};
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(WinRtDatagram)
-Q_DECLARE_METATYPE(WinRTSocketEngine::ErrorString)
-
-#endif // QNATIVESOCKETENGINE_WINRT_P_H
diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h
index e038352352..a172a14a10 100644
--- a/src/network/socket/qnet_unix_p.h
+++ b/src/network/socket/qnet_unix_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNET_UNIX_P_H
#define QNET_UNIX_P_H
@@ -144,7 +108,7 @@ static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SO
{
int ret;
// Solaris e.g. expects a non-const 2nd parameter
- EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen));
+ QT_EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen));
return ret;
}
#undef QT_SOCKET_CONNECT
@@ -160,15 +124,10 @@ static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SO
# undef listen
#endif
-// VxWorks' headers specify 'int' instead of '...' for the 3rd ioctl() parameter.
template <typename T>
static inline int qt_safe_ioctl(int sockfd, unsigned long request, T arg)
{
-#ifdef Q_OS_VXWORKS
- return ::ioctl(sockfd, request, (int) arg);
-#else
return ::ioctl(sockfd, request, arg);
-#endif
}
static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags)
@@ -180,7 +139,7 @@ static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flag
#endif
int ret;
- EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
+ QT_EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
return ret;
}
@@ -188,7 +147,7 @@ static inline int qt_safe_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
int ret;
- EINTR_LOOP(ret, ::recvmsg(sockfd, msg, flags));
+ QT_EINTR_LOOP(ret, ::recvmsg(sockfd, msg, flags));
return ret;
}
diff --git a/src/network/socket/qsctpserver.cpp b/src/network/socket/qsctpserver.cpp
index 2aa694b3fd..cd060d93e8 100644
--- a/src/network/socket/qsctpserver.cpp
+++ b/src/network/socket/qsctpserver.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QSCTPSERVER_DEBUG
@@ -82,7 +46,7 @@
between endpoints. Call nextPendingDatagramConnection() to accept
the pending datagram-mode connection as a connected QSctpSocket.
- \note This feature is not supported on the Windows platform.
+ \note This class is not supported on the Windows platform.
\sa QTcpServer, QSctpSocket, QAbstractSocket
*/
diff --git a/src/network/socket/qsctpserver.h b/src/network/socket/qsctpserver.h
index b678ba053d..f82ae71f16 100644
--- a/src/network/socket/qsctpserver.h
+++ b/src/network/socket/qsctpserver.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSCTPSERVER_H
#define QSCTPSERVER_H
@@ -45,7 +9,7 @@
QT_BEGIN_NAMESPACE
-#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_SCTP) || defined(Q_QDOC)
class QSctpServerPrivate;
class QSctpSocket;
diff --git a/src/network/socket/qsctpserver_p.h b/src/network/socket/qsctpserver_p.h
index 8816cc150e..a4200547ea 100644
--- a/src/network/socket/qsctpserver_p.h
+++ b/src/network/socket/qsctpserver_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSCTPSERVER_P_H
#define QSCTPSERVER_P_H
diff --git a/src/network/socket/qsctpsocket.cpp b/src/network/socket/qsctpsocket.cpp
index fe76f64c42..868c9e3785 100644
--- a/src/network/socket/qsctpsocket.cpp
+++ b/src/network/socket/qsctpsocket.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QSCTPSOCKET_DEBUG
@@ -110,7 +74,7 @@
etc. is allowed in datagram mode with the same limitations as in
continuous byte stream mode.
- \note This feature is not supported on the Windows platform.
+ \note This class is not supported on the Windows platform.
\sa QSctpServer, QTcpSocket, QAbstractSocket
*/
@@ -119,7 +83,6 @@
#include "qsctpsocket_p.h"
#include "qabstractsocketengine_p.h"
-#include "private/qbytearray_p.h"
#ifdef QSCTPSOCKET_DEBUG
#include <qdebug.h>
@@ -156,7 +119,7 @@ bool QSctpSocketPrivate::canReadNotification()
const int savedCurrentChannel = currentReadChannel;
bool currentChannelRead = false;
do {
- int datagramSize = incomingDatagram.size();
+ qsizetype datagramSize = incomingDatagram.size();
QIpPacketHeader header;
do {
@@ -169,7 +132,7 @@ bool QSctpSocketPrivate::canReadNotification()
bytesToRead = 4096;
}
- Q_ASSERT((datagramSize + int(bytesToRead)) < MaxByteArraySize);
+ Q_ASSERT((datagramSize + qsizetype(bytesToRead)) < QByteArray::max_size());
incomingDatagram.resize(datagramSize + int(bytesToRead));
#if defined (QSCTPSOCKET_DEBUG)
@@ -545,3 +508,5 @@ bool QSctpSocket::writeDatagram(const QNetworkDatagram &datagram)
}
QT_END_NAMESPACE
+
+#include "moc_qsctpsocket.cpp"
diff --git a/src/network/socket/qsctpsocket.h b/src/network/socket/qsctpsocket.h
index 5288da6129..8f0578ac26 100644
--- a/src/network/socket/qsctpsocket.h
+++ b/src/network/socket/qsctpsocket.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSCTPSOCKET_H
#define QSCTPSOCKET_H
@@ -45,7 +9,7 @@
QT_BEGIN_NAMESPACE
-#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_SCTP) || defined(Q_QDOC)
class QSctpSocketPrivate;
diff --git a/src/network/socket/qsctpsocket_p.h b/src/network/socket/qsctpsocket_p.h
index 8f9413cb47..bcc407eb67 100644
--- a/src/network/socket/qsctpsocket_p.h
+++ b/src/network/socket/qsctpsocket_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSCTPSOCKET_P_H
#define QSCTPSOCKET_P_H
@@ -54,7 +18,7 @@
#include <QtNetwork/qsctpsocket.h>
#include <private/qtcpsocket_p.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
#include <private/qnetworkdatagram_p.h>
#include <deque>
@@ -77,8 +41,8 @@ public:
int maximumChannelCount;
typedef std::deque<QIpPacketHeader> IpHeaderList;
- QVector<IpHeaderList> readHeaders;
- QVector<IpHeaderList> writeHeaders;
+ QList<IpHeaderList> readHeaders;
+ QList<IpHeaderList> writeHeaders;
void configureCreatedSocket() override;
};
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 622d5df131..b0fdc63d66 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsocks5socketengine_p.h"
@@ -45,6 +9,7 @@
#include "qdebug.h"
#include "qhash.h"
#include "qqueue.h"
+#include "qdeadlinetimer.h"
#include "qelapsedtimer.h"
#include "qmutex.h"
#include "qthread.h"
@@ -56,14 +21,21 @@
#include <qendian.h>
#include <qnetworkinterface.h>
+#include <QtCore/qpointer.h>
+
+#include <memory>
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
static const int MaxWriteBufferSize = 128*1024;
//#define QSOCKS5SOCKETLAYER_DEBUG
#define MAX_DATA_DUMP 256
-#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
+static constexpr auto Socks5BlockingBindTimeout = 5s;
#define Q_INIT_CHECK(returnValue) do { \
if (!d->data) { \
@@ -100,35 +72,35 @@ static const int MaxWriteBufferSize = 128*1024;
static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State s)
{
switch (s) {
- case QSocks5SocketEnginePrivate::Uninitialized: return QLatin1String("Uninitialized");
- case QSocks5SocketEnginePrivate::ConnectError: return QLatin1String("ConnectError");
- case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return QLatin1String("AuthenticationMethodsSent");
- case QSocks5SocketEnginePrivate::Authenticating: return QLatin1String("Authenticating");
- case QSocks5SocketEnginePrivate::AuthenticatingError: return QLatin1String("AuthenticatingError");
- case QSocks5SocketEnginePrivate::RequestMethodSent: return QLatin1String("RequestMethodSent");
- case QSocks5SocketEnginePrivate::RequestError: return QLatin1String("RequestError");
- case QSocks5SocketEnginePrivate::Connected: return QLatin1String("Connected");
- case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return QLatin1String("UdpAssociateSuccess");
- case QSocks5SocketEnginePrivate::BindSuccess: return QLatin1String("BindSuccess");
- case QSocks5SocketEnginePrivate::ControlSocketError: return QLatin1String("ControlSocketError");
- case QSocks5SocketEnginePrivate::SocksError: return QLatin1String("SocksError");
- case QSocks5SocketEnginePrivate::HostNameLookupError: return QLatin1String("HostNameLookupError");
+ case QSocks5SocketEnginePrivate::Uninitialized: return "Uninitialized"_L1;
+ case QSocks5SocketEnginePrivate::ConnectError: return "ConnectError"_L1;
+ case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return "AuthenticationMethodsSent"_L1;
+ case QSocks5SocketEnginePrivate::Authenticating: return "Authenticating"_L1;
+ case QSocks5SocketEnginePrivate::AuthenticatingError: return "AuthenticatingError"_L1;
+ case QSocks5SocketEnginePrivate::RequestMethodSent: return "RequestMethodSent"_L1;
+ case QSocks5SocketEnginePrivate::RequestError: return "RequestError"_L1;
+ case QSocks5SocketEnginePrivate::Connected: return "Connected"_L1;
+ case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return "UdpAssociateSuccess"_L1;
+ case QSocks5SocketEnginePrivate::BindSuccess: return "BindSuccess"_L1;
+ case QSocks5SocketEnginePrivate::ControlSocketError: return "ControlSocketError"_L1;
+ case QSocks5SocketEnginePrivate::SocksError: return "SocksError"_L1;
+ case QSocks5SocketEnginePrivate::HostNameLookupError: return "HostNameLookupError"_L1;
default: break;
}
- return QLatin1String("unknown state");
+ return "unknown state"_L1;
}
static QString dump(const QByteArray &buf)
{
QString data;
for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
- if (i) data += QLatin1Char(' ');
+ if (i) data += u' ';
uint val = (unsigned char)buf.at(i);
- // data += QString("0x%1").arg(val, 3, 16, QLatin1Char('0'));
+ // data += QString("0x%1").arg(val, 3, 16, u'0');
data += QString::number(val);
}
if (buf.size() > MAX_DATA_DUMP)
- data += QLatin1String(" ...");
+ data += " ..."_L1;
return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
}
@@ -144,7 +116,7 @@ static inline QString dump(const QByteArray &) { return QString(); }
/*
inserts the host address in buf at pos and updates pos.
- if the func fails the data in buf and the vallue of pos is undefined
+ if the func fails the data in buf and the value of pos is undefined
*/
static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf)
{
@@ -186,11 +158,11 @@ static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 po
QByteArray encodedHostName = QUrl::toAce(hostname);
QByteArray &buf = *pBuf;
- if (encodedHostName.length() > 255)
+ if (encodedHostName.size() > 255)
return false;
buf.append(S5_DOMAINNAME);
- buf.append(uchar(encodedHostName.length()));
+ buf.append(uchar(encodedHostName.size()));
buf.append(encodedHostName);
// add port
@@ -206,7 +178,7 @@ static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 po
/*
- retrives the host address in buf at pos and updates pos.
+ retrieves the host address in buf at pos and updates pos.
return 1 if OK, 0 if need more data, -1 if error
if the func fails the value of the address and the pos is undefined
*/
@@ -325,7 +297,7 @@ protected:
QRecursiveMutex mutex;
int sweepTimerId = -1;
//socket descriptor, data, timestamp
- QHash<int, QSocks5BindData *> store;
+ QHash<qintptr, QSocks5BindData *> store;
};
Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
@@ -349,9 +321,10 @@ void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
}
bindData->timeStamp.start();
store.insert(socketDescriptor, bindData);
+
// start sweep timer if not started
if (sweepTimerId == -1)
- sweepTimerId = startTimer(60000);
+ sweepTimerId = startTimer(1min);
}
bool QSocks5BindStore::contains(qintptr socketDescriptor)
@@ -492,7 +465,7 @@ bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool
QString QSocks5PasswordAuthenticator::errorString()
{
- return QLatin1String("Socks5 user name or password incorrect");
+ return "Socks5 user name or password incorrect"_L1;
}
@@ -535,9 +508,6 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
udpData = new QSocks5UdpAssociateData;
data = udpData;
udpData->udpSocket = new QUdpSocket(q);
-#ifndef QT_NO_BEARERMANAGEMENT
- udpData->udpSocket->setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
q, SLOT(_q_udpSocketReadNotification()),
@@ -549,9 +519,6 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
}
data->controlSocket = new QTcpSocket(q);
-#ifndef QT_NO_BEARERMANAGEMENT
- data->controlSocket->setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
data->controlSocket->setProxy(QNetworkProxy::NoProxy);
QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
Qt::DirectConnection);
@@ -559,8 +526,8 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
Qt::DirectConnection);
QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
Qt::DirectConnection);
- QObject::connect(data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)),
- q, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
+ QObject::connect(data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
+ q, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
Qt::DirectConnection);
QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
Qt::DirectConnection);
@@ -753,7 +720,7 @@ void QSocks5SocketEnginePrivate::parseAuthenticationMethodReply()
return;
} else if (buf.at(1) != data->authenticator->methodId()
|| !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
- setErrorState(AuthenticatingError, QLatin1String("Socks5 host did not support authentication method."));
+ setErrorState(AuthenticatingError, "Socks5 host did not support authentication method."_L1);
socketError = QAbstractSocket::SocketAccessError; // change the socket error
emitConnectionNotification();
return;
@@ -1056,7 +1023,7 @@ bool QSocks5SocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket::
Qt::DirectConnection);
QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
Qt::DirectConnection);
- QObject::connect(d->data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
+ QObject::connect(d->data->controlSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketErrorOccurred(QAbstractSocket::SocketError)),
Qt::DirectConnection);
QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
Qt::DirectConnection);
@@ -1106,7 +1073,7 @@ bool QSocks5SocketEngine::connectInternal()
} else if (socketType() == QAbstractSocket::UdpSocket) {
d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
// all udp needs to be bound
- if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0))
+ if (!bind(QHostAddress("0.0.0.0"_L1), 0))
return false;
setState(QAbstractSocket::ConnectedState);
@@ -1196,7 +1163,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
}
if (buf.size()) {
QSOCKS5_DEBUG << dump(buf);
- connectData->readBuffer.append(buf);
+ connectData->readBuffer.append(std::move(buf));
emitReadNotification();
}
break;
@@ -1231,7 +1198,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketBytesWritten()
}
}
-void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketError error)
+void QSocks5SocketEnginePrivate::_q_controlSocketErrorOccurred(QAbstractSocket::SocketError error)
{
QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
@@ -1296,7 +1263,7 @@ void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
int pos = 0;
const char *buf = inBuf.constData();
if (inBuf.size() < 4) {
- QSOCKS5_D_DEBUG << "bugus udp data, discarding";
+ QSOCKS5_D_DEBUG << "bogus udp data, discarding";
return;
}
QSocks5RevivedDatagram datagram;
@@ -1323,7 +1290,7 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
{
Q_D(QSocks5SocketEngine);
- // when bind wee will block until the bind is finished as the info from the proxy server is needed
+ // when bind we will block until the bind is finished as the info from the proxy server is needed
QHostAddress address;
if (addr.protocol() == QAbstractSocket::AnyIPProtocol)
@@ -1363,11 +1330,8 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
return false;
}
- int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
- QElapsedTimer stopWatch;
- stopWatch.start();
d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
- if (!d->waitForConnected(msecs, nullptr) ||
+ if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
// waitForConnected sets the error state and closes the socket
QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
@@ -1389,7 +1353,7 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
// binding timed out
setError(QAbstractSocket::SocketTimeoutError,
- QLatin1String(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
+ QLatin1StringView(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
///### delete d->udpSocket;
///### d->udpSocket = 0;
@@ -1397,9 +1361,10 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
}
-bool QSocks5SocketEngine::listen()
+bool QSocks5SocketEngine::listen(int backlog)
{
Q_D(QSocks5SocketEngine);
+ Q_UNUSED(backlog);
QSOCKS5_Q_DEBUG << "listen()";
@@ -1416,7 +1381,7 @@ bool QSocks5SocketEngine::listen()
return false;
}
-int QSocks5SocketEngine::accept()
+qintptr QSocks5SocketEngine::accept()
{
Q_D(QSocks5SocketEngine);
// check we are listing ---
@@ -1442,10 +1407,10 @@ int QSocks5SocketEngine::accept()
d->socketState = QAbstractSocket::UnconnectedState;
break;
case QSocks5SocketEnginePrivate::ControlSocketError:
- setError(QAbstractSocket::ProxyProtocolError, QLatin1String("Control socket error"));
+ setError(QAbstractSocket::ProxyProtocolError, "Control socket error"_L1);
break;
default:
- setError(QAbstractSocket::ProxyProtocolError, QLatin1String("SOCKS5 proxy error"));
+ setError(QAbstractSocket::ProxyProtocolError, "SOCKS5 proxy error"_L1);
break;
}
return sd;
@@ -1457,11 +1422,9 @@ void QSocks5SocketEngine::close()
Q_D(QSocks5SocketEngine);
if (d->data && d->data->controlSocket) {
if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
- int msecs = 100;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline(100ms);
while (!d->data->controlSocket->bytesToWrite()) {
- if (!d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed())))
+ if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
break;
}
}
@@ -1482,7 +1445,7 @@ qint64 QSocks5SocketEngine::bytesAvailable() const
#ifndef QT_NO_UDPSOCKET
else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
&& !d->udpData->pendingDatagrams.isEmpty())
- return d->udpData->pendingDatagrams.first().data.size();
+ return d->udpData->pendingDatagrams.constFirst().data.size();
#endif
return 0;
}
@@ -1497,7 +1460,7 @@ qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
//imitate remote closed
close();
setError(QAbstractSocket::RemoteHostClosedError,
- QLatin1String("Remote host closed connection###"));
+ "Remote host closed connection"_L1);
setState(QAbstractSocket::UnconnectedState);
return -1;
} else {
@@ -1531,8 +1494,12 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
if (!d->data->authenticator->seal(buf, &sealedBuf)) {
// ### Handle this error.
}
+ // We pass pointer and size because 'sealedBuf' is (most definitely) raw data:
+ // QIODevice might have to cache the byte array if the socket cannot write the data.
+ // If the _whole_ array needs to be cached then it would simply store a copy of the
+ // array whose data will go out of scope and be deallocated before it can be used.
+ qint64 written = d->data->controlSocket->write(sealedBuf.constData(), sealedBuf.size());
- qint64 written = d->data->controlSocket->write(sealedBuf);
if (written <= 0) {
QSOCKS5_Q_DEBUG << "native write returned" << written;
return written;
@@ -1556,7 +1523,7 @@ bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
const QNetworkInterface &)
{
setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Operation on socket is not supported"));
+ "Operation on socket is not supported"_L1);
return false;
}
@@ -1564,7 +1531,7 @@ bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
const QNetworkInterface &)
{
setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Operation on socket is not supported"));
+ "Operation on socket is not supported"_L1);
return false;
}
@@ -1577,7 +1544,7 @@ QNetworkInterface QSocks5SocketEngine::multicastInterface() const
bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
{
setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Operation on socket is not supported"));
+ "Operation on socket is not supported"_L1);
return false;
}
#endif // QT_NO_NETWORKINTERFACE
@@ -1617,9 +1584,9 @@ qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea
}
return copyLen;
#else
- Q_UNUSED(data)
- Q_UNUSED(maxlen)
- Q_UNUSED(header)
+ Q_UNUSED(data);
+ Q_UNUSED(maxlen);
+ Q_UNUSED(header);
return -1;
#endif // QT_NO_UDPSOCKET
}
@@ -1633,7 +1600,7 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI
if (!d->data) {
d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
// all udp needs to be bound
- if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0)) {
+ if (!bind(QHostAddress("0.0.0.0"_L1), 0)) {
//### set error
return -1;
}
@@ -1641,10 +1608,12 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI
QByteArray outBuf;
outBuf.reserve(270 + len);
- outBuf[0] = 0x00;
- outBuf[1] = 0x00;
- outBuf[2] = 0x00;
+ outBuf.append(3, '\0');
if (!qt_socks5_set_host_address_and_port(header.destinationAddress, header.destinationPort, &outBuf)) {
+ QSOCKS5_DEBUG << "error setting address" << header.destinationAddress << " : "
+ << header.destinationPort;
+ //### set error code ....
+ return -1;
}
outBuf += QByteArray(data, len);
QSOCKS5_DEBUG << "sending" << dump(outBuf);
@@ -1664,9 +1633,9 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI
return len;
#else
- Q_UNUSED(data)
- Q_UNUSED(len)
- Q_UNUSED(header)
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ Q_UNUSED(header);
return -1;
#endif // QT_NO_UDPSOCKET
}
@@ -1708,7 +1677,7 @@ bool QSocks5SocketEngine::setOption(SocketOption option, int value)
return false;
}
-bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
+bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
{
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return false;
@@ -1718,11 +1687,8 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
mode == BindMode ? BindSuccess :
UdpAssociateSuccess;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
while (socks5State != wantedState) {
- if (!data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1736,18 +1702,15 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
return true;
}
-bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
- QSOCKS5_DEBUG << "waitForRead" << msecs;
+ QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
d->readNotificationActivated = false;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
// are we connected yet?
- if (!d->waitForConnected(msecs, timedOut))
+ if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1761,7 +1724,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
d->mode == QSocks5SocketEnginePrivate::BindMode) {
while (!d->readNotificationActivated) {
- if (!d->data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1774,7 +1737,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
#ifndef QT_NO_UDPSOCKET
} else {
while (!d->readNotificationActivated) {
- if (!d->udpData->udpSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
*timedOut = true;
@@ -1793,16 +1756,13 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
}
-bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
- QSOCKS5_DEBUG << "waitForWrite" << msecs;
-
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
// are we connected yet?
- if (!d->waitForConnected(msecs, timedOut))
+ if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1811,27 +1771,32 @@ bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
// flush any bytes we may still have buffered in the time that we have left
if (d->data->controlSocket->bytesToWrite())
- d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
- while ((msecs == -1 || stopWatch.elapsed() < msecs)
- && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
- && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
- d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
+ d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
+
+ auto shouldWriteBytes = [&]() {
+ return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
+ && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
+ };
+
+ qint64 remainingTime = deadline.remainingTime();
+ for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
+ d->data->controlSocket->waitForBytesWritten(remainingTime);
return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
}
bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
- bool canRead = waitForRead(msecs, timedOut);
+ bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
- bool canWrite = waitForWrite(msecs, timedOut);
+ bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;
@@ -1911,9 +1876,9 @@ QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socke
QSOCKS5_DEBUG << "not proxying";
return nullptr;
}
- QScopedPointer<QSocks5SocketEngine> engine(new QSocks5SocketEngine(parent));
+ auto engine = std::make_unique<QSocks5SocketEngine>(parent);
engine->setProxy(proxy);
- return engine.take();
+ return engine.release();
}
QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr socketDescriptor, QObject *parent)
@@ -1927,3 +1892,5 @@ QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(qintptr so
}
QT_END_NAMESPACE
+
+#include "moc_qsocks5socketengine_p.cpp"
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index c256987e2d..3a169812df 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSOCKS5SOCKETENGINE_P_H
#define QSOCKS5SOCKETENGINE_P_H
@@ -52,8 +16,10 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qnetworkproxy.h>
+
#include "qabstractsocketengine_p.h"
-#include "qnetworkproxy.h"
QT_REQUIRE_CONFIG(socks5);
@@ -81,8 +47,8 @@ public:
bool connectToHost(const QHostAddress &address, quint16 port) override;
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
- bool listen() override;
- int accept() override;
+ bool listen(int backlog) override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -112,11 +78,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -130,7 +99,7 @@ private:
Q_DISABLE_COPY_MOVE(QSocks5SocketEngine)
Q_PRIVATE_SLOT(d_func(), void _q_controlSocketConnected())
Q_PRIVATE_SLOT(d_func(), void _q_controlSocketReadNotification())
- Q_PRIVATE_SLOT(d_func(), void _q_controlSocketError(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketErrorOccurred(QAbstractSocket::SocketError))
#ifndef QT_NO_UDPSOCKET
Q_PRIVATE_SLOT(d_func(), void _q_udpSocketReadNotification())
#endif
@@ -155,9 +124,9 @@ public:
virtual bool beginAuthenticate(QTcpSocket *socket, bool *completed);
virtual bool continueAuthenticate(QTcpSocket *socket, bool *completed);
- virtual bool seal(const QByteArray &buf, QByteArray *sealedBuf);
- virtual bool unSeal(const QByteArray &sealedBuf, QByteArray *buf);
- virtual bool unSeal(QTcpSocket *sealedSocket, QByteArray *buf);
+ bool seal(const QByteArray &buf, QByteArray *sealedBuf);
+ bool unSeal(const QByteArray &sealedBuf, QByteArray *buf);
+ bool unSeal(QTcpSocket *sealedSocket, QByteArray *buf);
virtual QString errorString() { return QString(); }
};
@@ -242,11 +211,11 @@ public:
void parseRequestMethodReply();
void parseNewConnection();
- bool waitForConnected(int msecs, bool *timedOut);
+ bool waitForConnected(QDeadlineTimer deadline, bool *timedOut);
void _q_controlSocketConnected();
void _q_controlSocketReadNotification();
- void _q_controlSocketError(QAbstractSocket::SocketError);
+ void _q_controlSocketErrorOccurred(QAbstractSocket::SocketError);
#ifndef QT_NO_UDPSOCKET
void _q_udpSocketReadNotification();
#endif
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index 9916c75e65..a0c0f00aaa 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QTCPSERVER_DEBUG
@@ -54,7 +18,9 @@
Call listen() to have the server listen for incoming connections.
The newConnection() signal is then emitted each time a client
- connects to the server.
+ connects to the server. When the client connection has been added
+ to the pending connection queue using the addPendingConnection()
+ function, the pendingConnectionAvailable() signal is emitted.
Call nextPendingConnection() to accept the pending connection as
a connected QTcpSocket. The function returns a pointer to a
@@ -77,17 +43,27 @@
use waitForNewConnection(), which blocks until either a
connection is available or a timeout expires.
- \sa QTcpSocket, {Fortune Server Example}, {Threaded Fortune Server Example},
- {Loopback Example}, {Torrent Example}
+ \sa QTcpSocket, {Fortune Server}, {Threaded Fortune Server},
+ {Torrent Example}
*/
/*! \fn void QTcpServer::newConnection()
- This signal is emitted every time a new connection is available.
+ This signal is emitted every time a new connection is available, regardless
+ of whether it has been added to the pending connections queue or not.
\sa hasPendingConnections(), nextPendingConnection()
*/
+/*! \fn void QTcpServer::pendingConnectionAvailable()
+
+ This signal is emitted every time a new connection has been added to the
+ pending connections queue.
+
+ \sa hasPendingConnections(), nextPendingConnection()
+ \since 6.4
+*/
+
/*! \fn void QTcpServer::acceptError(QAbstractSocket::SocketError socketError)
\since 5.0
@@ -157,7 +133,7 @@ QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint
}
// return the first that we can use
- for (const QNetworkProxy &p : qAsConst(proxies)) {
+ for (const QNetworkProxy &p : std::as_const(proxies)) {
if (socketType == QAbstractSocket::TcpSocket &&
(p.capabilities() & QNetworkProxy::ListeningCapability) != 0)
return p;
@@ -196,7 +172,7 @@ void QTcpServerPrivate::readNotification()
{
Q_Q(QTcpServer);
for (;;) {
- if (pendingConnections.count() >= maxConnections) {
+ if (totalPendingConnections() >= maxConnections) {
#if defined (QTCPSERVER_DEBUG)
qDebug("QTcpServerPrivate::_q_processIncomingConnection() too many connections");
#endif
@@ -205,7 +181,7 @@ void QTcpServerPrivate::readNotification()
return;
}
- int descriptor = socketEngine->accept();
+ qintptr descriptor = socketEngine->accept();
if (descriptor == -1) {
if (socketEngine->error() != QAbstractSocket::TemporaryError) {
q->pauseAccepting();
@@ -218,16 +194,32 @@ void QTcpServerPrivate::readNotification()
#if defined (QTCPSERVER_DEBUG)
qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor);
#endif
+ QPointer<QTcpServer> that = q;
q->incomingConnection(descriptor);
- QPointer<QTcpServer> that = q;
- emit q->newConnection();
+ if (that)
+ emit q->newConnection();
+
if (!that || !q->isListening())
return;
}
}
/*!
+ \internal
+ Return the amount of sockets currently in queue for the server.
+ This is to make maxPendingConnections work properly with servers that don't
+ necessarily have 'ready-to-go' sockets as soon as they connect,
+ e.g. QSslServer.
+ By default we just return pendingConnections.size(), which is equivalent to
+ what it did before.
+*/
+int QTcpServerPrivate::totalPendingConnections() const
+{
+ return int(pendingConnections.size());
+}
+
+/*!
Constructs a QTcpServer object.
\a parent is passed to the QObject constructor.
@@ -314,10 +306,6 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port)
d->serverSocketErrorString = tr("Operation on socket is not supported");
return false;
}
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket engine (if it has been set)
- d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
-#endif
if (!d->socketEngine->initialize(d->socketType, proto)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
@@ -335,7 +323,7 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port)
return false;
}
- if (!d->socketEngine->listen()) {
+ if (!d->socketEngine->listen(d->listenBacklog)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
return false;
@@ -436,10 +424,6 @@ bool QTcpServer::setSocketDescriptor(qintptr socketDescriptor)
d->serverSocketErrorString = tr("Operation on socket is not supported");
return false;
}
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the socket engine (if it has been set)
- d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
-#endif
if (!d->socketEngine->initialize(socketDescriptor, QAbstractSocket::ListeningState)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
@@ -514,7 +498,7 @@ bool QTcpServer::waitForNewConnection(int msec, bool *timedOut)
if (d->state != QAbstractSocket::ListeningState)
return false;
- if (!d->socketEngine->waitForRead(msec, timedOut)) {
+ if (!d->socketEngine->waitForRead(QDeadlineTimer(msec), timedOut)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
return false;
@@ -616,14 +600,17 @@ void QTcpServer::incomingConnection(qintptr socketDescriptor)
\note Don't forget to call this member from reimplemented
incomingConnection() if you do not want to break the
- Pending Connections mechanism.
+ Pending Connections mechanism. This function emits the
+ pendingConnectionAvailable() signal after the socket has been
+ added.
- \sa incomingConnection()
+ \sa incomingConnection(), pendingConnectionAvailable()
\since 4.7
*/
void QTcpServer::addPendingConnection(QTcpSocket* socket)
{
d_func()->pendingConnections.append(socket);
+ emit pendingConnectionAvailable(QPrivateSignal());
}
/*!
@@ -658,6 +645,34 @@ int QTcpServer::maxPendingConnections() const
}
/*!
+ Sets the backlog queue size of to be accepted connections to \a
+ size. The operating system might reduce or ignore this value.
+ By default, the queue size is 50.
+
+ \note This property must be set prior to calling listen().
+
+ \since 6.3
+
+ \sa listenBacklogSize()
+*/
+void QTcpServer::setListenBacklogSize(int size)
+{
+ d_func()->listenBacklog = size;
+}
+
+/*!
+ Returns the backlog queue size of to be accepted connections.
+
+ \since 6.3
+
+ \sa setListenBacklogSize()
+*/
+int QTcpServer::listenBacklogSize() const
+{
+ return d_func()->listenBacklog;
+}
+
+/*!
Returns an error code for the last error that occurred.
\sa errorString()
diff --git a/src/network/socket/qtcpserver.h b/src/network/socket/qtcpserver.h
index 37df12919f..6177a3b0aa 100644
--- a/src/network/socket/qtcpserver.h
+++ b/src/network/socket/qtcpserver.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTCPSERVER_H
#define QTCPSERVER_H
@@ -69,6 +33,9 @@ public:
void setMaxPendingConnections(int numConnections);
int maxPendingConnections() const;
+ void setListenBacklogSize(int size);
+ int listenBacklogSize() const;
+
quint16 serverPort() const;
QHostAddress serverAddress() const;
@@ -99,6 +66,7 @@ protected:
Q_SIGNALS:
void newConnection();
+ void pendingConnectionAvailable(QPrivateSignal);
void acceptError(QAbstractSocket::SocketError socketError);
private:
diff --git a/src/network/socket/qtcpserver_p.h b/src/network/socket/qtcpserver_p.h
index 6ee8c5f0b1..853a4aaf96 100644
--- a/src/network/socket/qtcpserver_p.h
+++ b/src/network/socket/qtcpserver_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTCPSERVER_P_H
#define QTCPSERVER_P_H
@@ -83,6 +47,7 @@ public:
QAbstractSocket::SocketError serverSocketError;
QString serverSocketErrorString;
+ int listenBacklog = 50;
int maxConnections;
#ifndef QT_NO_NETWORKPROXY
@@ -91,6 +56,7 @@ public:
#endif
virtual void configureCreatedSocket();
+ virtual int totalPendingConnections() const;
// from QAbstractSocketEngineReceiver
void readNotification() override;
diff --git a/src/network/socket/qtcpsocket.cpp b/src/network/socket/qtcpsocket.cpp
index 85d425055b..979382f26c 100644
--- a/src/network/socket/qtcpsocket.cpp
+++ b/src/network/socket/qtcpsocket.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QTCPSOCKET_DEBUG
@@ -59,9 +23,9 @@
\note TCP sockets cannot be opened in QIODevice::Unbuffered mode.
\sa QTcpServer, QUdpSocket, QNetworkAccessManager,
- {Fortune Server Example}, {Fortune Client Example},
- {Threaded Fortune Server Example}, {Blocking Fortune Client Example},
- {Loopback Example}, {Torrent Example}
+ {Fortune Server}, {Fortune Client},
+ {Threaded Fortune Server}, {Blocking Fortune Client},
+ {Torrent Example}
*/
#include "qtcpsocket.h"
@@ -119,3 +83,5 @@ QTcpSocket::QTcpSocket(QAbstractSocket::SocketType socketType,
}
QT_END_NAMESPACE
+
+#include "moc_qtcpsocket.cpp"
diff --git a/src/network/socket/qtcpsocket.h b/src/network/socket/qtcpsocket.h
index b2c8bcc884..a1c610b69b 100644
--- a/src/network/socket/qtcpsocket.h
+++ b/src/network/socket/qtcpsocket.h
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTCPSOCKET_H
#define QTCPSOCKET_H
#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qhostaddress.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
@@ -56,13 +21,20 @@ public:
explicit QTcpSocket(QObject *parent = nullptr);
virtual ~QTcpSocket();
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_QDOC)
+ // ### Qt7: move into QAbstractSocket
+ using QAbstractSocket::bind;
+ bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
+ { return bind(QHostAddress(addr), port, mode); }
+#endif
+
protected:
QTcpSocket(QTcpSocketPrivate &dd, QObject *parent = nullptr);
QTcpSocket(QAbstractSocket::SocketType socketType, QTcpSocketPrivate &dd,
QObject *parent = nullptr);
private:
- Q_DISABLE_COPY(QTcpSocket)
+ Q_DISABLE_COPY_MOVE(QTcpSocket)
Q_DECLARE_PRIVATE(QTcpSocket)
};
diff --git a/src/network/socket/qtcpsocket_p.h b/src/network/socket/qtcpsocket_p.h
index ba1a0aa920..5823342f22 100644
--- a/src/network/socket/qtcpsocket_p.h
+++ b/src/network/socket/qtcpsocket_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTCPSOCKET_P_H
#define QTCPSOCKET_P_H
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index 0e3d516535..bfeea307b2 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QUDPSOCKET_DEBUG
@@ -418,7 +382,7 @@ qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
if (state() == UnconnectedState)
bind();
- qint64 sent = d->socketEngine->writeDatagram(datagram.d->data,
+ qint64 sent = d->socketEngine->writeDatagram(datagram.d->data.constData(),
datagram.d->data.size(),
datagram.d->header);
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
@@ -466,6 +430,7 @@ QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize)
qint64 readBytes = d->socketEngine->readDatagram(result.d->data.data(), maxSize, &result.d->header,
QAbstractSocketEngine::WantAll);
d->hasPendingData = false;
+ d->hasPendingDatagram = false;
d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0) {
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
@@ -479,7 +444,7 @@ QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize)
/*!
Receives a datagram no larger than \a maxSize bytes and stores
it in \a data. The sender's host address and port is stored in
- *\a address and *\a port (unless the pointers are 0).
+ *\a address and *\a port (unless the pointers are \nullptr).
Returns the size of the datagram on success; otherwise returns
-1.
@@ -515,6 +480,7 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
}
d->hasPendingData = false;
+ d->hasPendingDatagram = false;
d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0) {
if (readBytes == -2) {
@@ -531,3 +497,5 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
#endif // QT_NO_UDPSOCKET
QT_END_NAMESPACE
+
+#include "moc_qudpsocket.cpp"
diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h
index ce4429d1cd..3fd1d3710a 100644
--- a/src/network/socket/qudpsocket.h
+++ b/src/network/socket/qudpsocket.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QUDPSOCKET_H
#define QUDPSOCKET_H
@@ -60,6 +24,13 @@ public:
explicit QUdpSocket(QObject *parent = nullptr);
virtual ~QUdpSocket();
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_QDOC)
+ // ### Qt7: move into QAbstractSocket
+ using QAbstractSocket::bind;
+ bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
+ { return bind(QHostAddress(addr), port, mode); }
+#endif
+
#ifndef QT_NO_NETWORKINTERFACE
bool joinMulticastGroup(const QHostAddress &groupAddress);
bool joinMulticastGroup(const QHostAddress &groupAddress,
@@ -83,7 +54,7 @@ public:
{ return writeDatagram(datagram.constData(), datagram.size(), host, port); }
private:
- Q_DISABLE_COPY(QUdpSocket)
+ Q_DISABLE_COPY_MOVE(QUdpSocket)
Q_DECLARE_PRIVATE(QUdpSocket)
};
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
deleted file mode 100644
index c3a98ea31a..0000000000
--- a/src/network/socket/socket.pri
+++ /dev/null
@@ -1,91 +0,0 @@
-# Qt network socket
-
-HEADERS += socket/qabstractsocketengine_p.h \
- socket/qabstractsocket.h \
- socket/qabstractsocket_p.h \
- socket/qtcpsocket.h \
- socket/qudpsocket.h \
- socket/qtcpserver.h \
- socket/qtcpsocket_p.h \
- socket/qtcpserver_p.h
-
-SOURCES += socket/qabstractsocketengine.cpp \
- socket/qabstractsocket.cpp \
- socket/qtcpsocket.cpp \
- socket/qudpsocket.cpp \
- socket/qtcpserver.cpp
-
-# SOCK5 support.
-
-qtConfig(socks5) {
- HEADERS += \
- socket/qsocks5socketengine_p.h
- SOURCES += \
- socket/qsocks5socketengine.cpp
-}
-
-qtConfig(http) {
- HEADERS += \
- socket/qhttpsocketengine_p.h
- SOURCES += \
- socket/qhttpsocketengine.cpp
-}
-
-# SCTP support.
-
-qtConfig(sctp) {
- HEADERS += socket/qsctpserver.h \
- socket/qsctpserver_p.h \
- socket/qsctpsocket.h \
- socket/qsctpsocket_p.h
-
- SOURCES += socket/qsctpserver.cpp \
- socket/qsctpsocket.cpp
-}
-
-!winrt {
- SOURCES += socket/qnativesocketengine.cpp
- HEADERS += socket/qnativesocketengine_p.h
-}
-
-unix {
- SOURCES += socket/qnativesocketengine_unix.cpp
- HEADERS += socket/qnet_unix_p.h
-}
-
-# Suppress deprecation warnings with moc because MS headers have
-# invalid C/C++ code otherwise.
-msvc: QMAKE_MOC_OPTIONS += -D_WINSOCK_DEPRECATED_NO_WARNINGS
-
-win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp
-win32:!winrt: QMAKE_USE_PRIVATE += advapi32
-
-winrt {
- SOURCES += socket/qnativesocketengine_winrt.cpp
- HEADERS += socket/qnativesocketengine_winrt_p.h
-}
-
-qtConfig(localserver) {
- HEADERS += socket/qlocalserver.h \
- socket/qlocalserver_p.h \
- socket/qlocalsocket.h \
- socket/qlocalsocket_p.h
- SOURCES += socket/qlocalsocket.cpp \
- socket/qlocalserver.cpp
-
- integrity|winrt {
- SOURCES += socket/qlocalsocket_tcp.cpp \
- socket/qlocalserver_tcp.cpp
- DEFINES += QT_LOCALSOCKET_TCP
- } else: unix {
- SOURCES += socket/qlocalsocket_unix.cpp \
- socket/qlocalserver_unix.cpp
- } else: win32 {
- SOURCES += socket/qlocalsocket_win.cpp \
- socket/qlocalserver_win.cpp
- }
-}
-
-qtConfig(system-proxies) {
- DEFINES += QT_USE_SYSTEM_PROXIES
-}
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp
deleted file mode 100644
index 6558643386..0000000000
--- a/src/network/ssl/qasn1element.cpp
+++ /dev/null
@@ -1,379 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include "qasn1element_p.h"
-
-#include <QtCore/qdatastream.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qvector.h>
-#include <QDebug>
-
-#include <locale>
-
-QT_BEGIN_NAMESPACE
-
-typedef QMap<QByteArray, QByteArray> OidNameMap;
-static OidNameMap createOidMap()
-{
- OidNameMap oids;
- // used by unit tests
- oids.insert(oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
- oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST"));
- oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street"));
- return oids;
-}
-Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap()))
-
-static bool stringToNonNegativeInt(const QByteArray &asnString, int *val)
-{
- // Helper function for toDateTime(), which handles chunking of the original
- // string into smaller sub-components, so we expect the whole 'asnString' to
- // be a valid non-negative number.
- Q_ASSERT(val);
-
- // We want the C locale, as used by QByteArray; however, no leading sign is
- // allowed (which QByteArray would accept), so we have to check the data:
- const std::locale localeC;
- for (char v : asnString) {
- if (!std::isdigit(v, localeC))
- return false;
- }
-
- bool ok = false;
- *val = asnString.toInt(&ok);
- Q_ASSERT(ok && *val >= 0);
- return true;
-}
-
-QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value)
- : mType(type)
- , mValue(value)
-{
-}
-
-bool QAsn1Element::read(QDataStream &stream)
-{
- // type
- quint8 tmpType;
- stream >> tmpType;
- if (!tmpType)
- return false;
-
- // length
- qint64 length = 0;
- quint8 first;
- stream >> first;
- if (first & 0x80) {
- // long form
- const quint8 bytes = (first & 0x7f);
- if (bytes > 7)
- return false;
-
- quint8 b;
- for (int i = 0; i < bytes; i++) {
- stream >> b;
- length = (length << 8) | b;
- }
- } else {
- // short form
- length = (first & 0x7f);
- }
-
- // value
- QByteArray tmpValue;
- tmpValue.resize(length);
- int count = stream.readRawData(tmpValue.data(), tmpValue.size());
- if (count != length)
- return false;
-
- mType = tmpType;
- mValue.swap(tmpValue);
- return true;
-}
-
-bool QAsn1Element::read(const QByteArray &data)
-{
- QDataStream stream(data);
- return read(stream);
-}
-
-void QAsn1Element::write(QDataStream &stream) const
-{
- // type
- stream << mType;
-
- // length
- qint64 length = mValue.size();
- if (length >= 128) {
- // long form
- quint8 encodedLength = 0x80;
- QByteArray ba;
- while (length) {
- ba.prepend(quint8((length & 0xff)));
- length >>= 8;
- encodedLength += 1;
- }
- stream << encodedLength;
- stream.writeRawData(ba.data(), ba.size());
- } else {
- // short form
- stream << quint8(length);
- }
-
- // value
- stream.writeRawData(mValue.data(), mValue.size());
-}
-
-QAsn1Element QAsn1Element::fromBool(bool val)
-{
- return QAsn1Element(QAsn1Element::BooleanType,
- QByteArray(1, val ? 0xff : 0x00));
-}
-
-QAsn1Element QAsn1Element::fromInteger(unsigned int val)
-{
- QAsn1Element elem(QAsn1Element::IntegerType);
- while (val > 127) {
- elem.mValue.prepend(val & 0xff);
- val >>= 8;
- }
- elem.mValue.prepend(val & 0x7f);
- return elem;
-}
-
-QAsn1Element QAsn1Element::fromVector(const QVector<QAsn1Element> &items)
-{
- QAsn1Element seq;
- seq.mType = SequenceType;
- QDataStream stream(&seq.mValue, QIODevice::WriteOnly);
- for (QVector<QAsn1Element>::const_iterator it = items.cbegin(), end = items.cend(); it != end; ++it)
- it->write(stream);
- return seq;
-}
-
-QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id)
-{
- QAsn1Element elem;
- elem.mType = ObjectIdentifierType;
- const QList<QByteArray> bits = id.split('.');
- Q_ASSERT(bits.size() > 2);
- elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt()));
- for (int i = 2; i < bits.size(); ++i) {
- char buffer[std::numeric_limits<unsigned int>::digits / 7 + 2];
- char *pBuffer = buffer + sizeof(buffer);
- *--pBuffer = '\0';
- unsigned int node = bits[i].toUInt();
- *--pBuffer = quint8((node & 0x7f));
- node >>= 7;
- while (node) {
- *--pBuffer = quint8(((node & 0x7f) | 0x80));
- node >>= 7;
- }
- elem.mValue += pBuffer;
- }
- return elem;
-}
-
-bool QAsn1Element::toBool(bool *ok) const
-{
- if (*this == fromBool(true)) {
- if (ok)
- *ok = true;
- return true;
- } else if (*this == fromBool(false)) {
- if (ok)
- *ok = true;
- return false;
- } else {
- if (ok)
- *ok = false;
- return false;
- }
-}
-
-QDateTime QAsn1Element::toDateTime() const
-{
- if (mValue.endsWith('Z')) {
- if (mType == UtcTimeType && mValue.size() == 13) {
- int year = 0;
- if (!stringToNonNegativeInt(mValue.mid(0, 2), &year))
- return QDateTime();
- // RFC 2459: YY represents a year in the range [1950, 2049]
- return QDateTime(QDate(year < 50 ? 2000 + year : 1900 + year,
- mValue.mid(2, 2).toInt(),
- mValue.mid(4, 2).toInt()),
- QTime(mValue.mid(6, 2).toInt(),
- mValue.mid(8, 2).toInt(),
- mValue.mid(10, 2).toInt()),
- Qt::UTC);
- } else if (mType == GeneralizedTimeType && mValue.size() == 15) {
- return QDateTime(QDate(mValue.mid(0, 4).toInt(),
- mValue.mid(4, 2).toInt(),
- mValue.mid(6, 2).toInt()),
- QTime(mValue.mid(8, 2).toInt(),
- mValue.mid(10, 2).toInt(),
- mValue.mid(12, 2).toInt()),
- Qt::UTC);
- }
- }
- return QDateTime();
-}
-
-QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const
-{
- QMultiMap<QByteArray, QString> info;
- QAsn1Element elem;
- QDataStream issuerStream(mValue);
- while (elem.read(issuerStream) && elem.mType == QAsn1Element::SetType) {
- QAsn1Element issuerElem;
- QDataStream setStream(elem.mValue);
- if (issuerElem.read(setStream) && issuerElem.mType == QAsn1Element::SequenceType) {
- QVector<QAsn1Element> elems = issuerElem.toVector();
- if (elems.size() == 2) {
- const QByteArray key = elems.front().toObjectName();
- if (!key.isEmpty())
- info.insert(key, elems.back().toString());
- }
- }
- }
- return info;
-}
-
-qint64 QAsn1Element::toInteger(bool *ok) const
-{
- if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) {
- if (ok)
- *ok = false;
- return 0;
- }
-
- // NOTE: negative numbers are not handled
- if (mValue.at(0) & 0x80) {
- if (ok)
- *ok = false;
- return 0;
- }
-
- qint64 value = mValue.at(0) & 0x7f;
- for (int i = 1; i < mValue.size(); ++i)
- value = (value << 8) | quint8(mValue.at(i));
-
- if (ok)
- *ok = true;
- return value;
-}
-
-QVector<QAsn1Element> QAsn1Element::toVector() const
-{
- QVector<QAsn1Element> items;
- if (mType == SequenceType) {
- QAsn1Element elem;
- QDataStream stream(mValue);
- while (elem.read(stream))
- items << elem;
- }
- return items;
-}
-
-QByteArray QAsn1Element::toObjectId() const
-{
- QByteArray key;
- if (mType == ObjectIdentifierType && !mValue.isEmpty()) {
- quint8 b = mValue.at(0);
- key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40);
- unsigned int val = 0;
- for (int i = 1; i < mValue.size(); ++i) {
- b = mValue.at(i);
- val = (val << 7) | (b & 0x7f);
- if (!(b & 0x80)) {
- key += '.' + QByteArray::number(val);
- val = 0;
- }
- }
- }
- return key;
-}
-
-QByteArray QAsn1Element::toObjectName() const
-{
- QByteArray key = toObjectId();
- return oidNameMap->value(key, key);
-}
-
-QString QAsn1Element::toString() const
-{
- // Detect embedded NULs and reject
- if (qstrlen(mValue) < uint(mValue.size()))
- return QString();
-
- if (mType == PrintableStringType || mType == TeletexStringType
- || mType == Rfc822NameType || mType == DnsNameType
- || mType == UniformResourceIdentifierType)
- return QString::fromLatin1(mValue, mValue.size());
- if (mType == Utf8StringType)
- return QString::fromUtf8(mValue, mValue.size());
-
- return QString();
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
deleted file mode 100644
index 020b5aa1af..0000000000
--- a/src/network/ssl/qasn1element_p.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#ifndef QASN1ELEMENT_P_H
-#define QASN1ELEMENT_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qmap.h>
-
-QT_BEGIN_NAMESPACE
-
-// General
-#define RSADSI_OID "1.2.840.113549."
-
-#define RSA_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.1.1")
-#define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1")
-#define EC_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10045.2.1")
-#define DH_ENCRYPTION_OID QByteArrayLiteral(RSADSI_OID "1.3.1")
-
-// These are mostly from the RFC for PKCS#5
-// PKCS#5: https://tools.ietf.org/html/rfc8018#appendix-B
-#define PKCS5_OID RSADSI_OID "1.5."
-// PKCS#12: https://tools.ietf.org/html/rfc7292#appendix-D)
-#define PKCS12_OID RSADSI_OID "1.12."
-
-// -PBES1
-#define PKCS5_MD2_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "1") // Not (yet) implemented
-#define PKCS5_MD2_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "4") // Not (yet) implemented
-#define PKCS5_MD5_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "3")
-#define PKCS5_MD5_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "6")
-#define PKCS5_SHA1_DES_CBC_OID QByteArrayLiteral(PKCS5_OID "10")
-#define PKCS5_SHA1_RC2_CBC_OID QByteArrayLiteral(PKCS5_OID "11")
-#define PKCS12_SHA1_RC4_128_OID QByteArrayLiteral(PKCS12_OID "1.1") // Not (yet) implemented
-#define PKCS12_SHA1_RC4_40_OID QByteArrayLiteral(PKCS12_OID "1.2") // Not (yet) implemented
-#define PKCS12_SHA1_3KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.3")
-#define PKCS12_SHA1_2KEY_3DES_CBC_OID QByteArrayLiteral(PKCS12_OID "1.4")
-#define PKCS12_SHA1_RC2_128_CBC_OID QByteArrayLiteral(PKCS12_OID "1.5")
-#define PKCS12_SHA1_RC2_40_CBC_OID QByteArrayLiteral(PKCS12_OID "1.6")
-
-// -PBKDF2
-#define PKCS5_PBKDF2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "12")
-
-// -PBES2
-#define PKCS5_PBES2_ENCRYPTION_OID QByteArrayLiteral(PKCS5_OID "13")
-
-// Digest
-#define DIGEST_ALGORITHM_OID RSADSI_OID "2."
-// -HMAC-SHA-1
-#define HMAC_WITH_SHA1 QByteArrayLiteral(DIGEST_ALGORITHM_OID "7")
-// -HMAC-SHA-2
-#define HMAC_WITH_SHA224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "8")
-#define HMAC_WITH_SHA256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "9")
-#define HMAC_WITH_SHA384 QByteArrayLiteral(DIGEST_ALGORITHM_OID "10")
-#define HMAC_WITH_SHA512 QByteArrayLiteral(DIGEST_ALGORITHM_OID "11")
-#define HMAC_WITH_SHA512_224 QByteArrayLiteral(DIGEST_ALGORITHM_OID "12")
-#define HMAC_WITH_SHA512_256 QByteArrayLiteral(DIGEST_ALGORITHM_OID "13")
-
-// Encryption algorithms
-#define ENCRYPTION_ALGORITHM_OID RSADSI_OID "3."
-#define DES_CBC_ENCRYPTION_OID QByteArrayLiteral("1.3.14.3.2.7")
-#define DES_EDE3_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "7")
-#define RC2_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "2")
-#define RC5_CBC_ENCRYPTION_OID QByteArrayLiteral(ENCRYPTION_ALGORITHM_OID "9") // Not (yet) implemented
-#define AES_OID "2.16.840.1.101.3.4.1."
-#define AES128_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "2")
-#define AES192_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "22") // Not (yet) implemented
-#define AES256_CBC_ENCRYPTION_OID QByteArrayLiteral(AES_OID "42") // Not (yet) implemented
-
-class Q_AUTOTEST_EXPORT QAsn1Element
-{
-public:
- enum ElementType {
- // universal
- BooleanType = 0x01,
- IntegerType = 0x02,
- BitStringType = 0x03,
- OctetStringType = 0x04,
- NullType = 0x05,
- ObjectIdentifierType = 0x06,
- Utf8StringType = 0x0c,
- PrintableStringType = 0x13,
- TeletexStringType = 0x14,
- UtcTimeType = 0x17,
- GeneralizedTimeType = 0x18,
- SequenceType = 0x30,
- SetType = 0x31,
-
- // GeneralNameTypes
- Rfc822NameType = 0x81,
- DnsNameType = 0x82,
- UniformResourceIdentifierType = 0x86,
- IpAddressType = 0x87,
-
- // context specific
- Context0Type = 0xA0,
- Context1Type = 0xA1,
- Context3Type = 0xA3
- };
-
- explicit QAsn1Element(quint8 type = 0, const QByteArray &value = QByteArray());
- bool read(QDataStream &data);
- bool read(const QByteArray &data);
- void write(QDataStream &data) const;
-
- static QAsn1Element fromBool(bool val);
- static QAsn1Element fromInteger(unsigned int val);
- static QAsn1Element fromVector(const QVector<QAsn1Element> &items);
- static QAsn1Element fromObjectId(const QByteArray &id);
-
- bool toBool(bool *ok = nullptr) const;
- QDateTime toDateTime() const;
- QMultiMap<QByteArray, QString> toInfo() const;
- qint64 toInteger(bool *ok = nullptr) const;
- QVector<QAsn1Element> toVector() const;
- QByteArray toObjectId() const;
- QByteArray toObjectName() const;
- QString toString() const;
-
- quint8 type() const { return mType; }
- QByteArray value() const { return mValue; }
-
- friend inline bool operator==(const QAsn1Element &, const QAsn1Element &);
- friend inline bool operator!=(const QAsn1Element &, const QAsn1Element &);
-
-private:
- quint8 mType;
- QByteArray mValue;
-};
-Q_DECLARE_TYPEINFO(QAsn1Element, Q_MOVABLE_TYPE);
-
-inline bool operator==(const QAsn1Element &e1, const QAsn1Element &e2)
-{ return e1.mType == e2.mType && e1.mValue == e2.mValue; }
-
-inline bool operator!=(const QAsn1Element &e1, const QAsn1Element &e2)
-{ return e1.mType != e2.mType || e1.mValue != e2.mValue; }
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp
index a2280a7d10..38ce144c8a 100644
--- a/src/network/ssl/qdtls.cpp
+++ b/src/network/ssl/qdtls.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsslconfiguration.h"
-#include "qdtls_openssl_p.h"
+#include "qsslsocket_p.h"
#include "qudpsocket.h"
+#include "qsslcipher.h"
#include "qdtls_p.h"
#include "qssl_p.h"
#include "qdtls.h"
@@ -57,7 +22,7 @@
The QDtlsClientVerifier class implements server-side DTLS cookie generation
and verification. Datagram security protocols are highly susceptible to a
- variety of Denial-of-Service attacks. According to \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1},
+ variety of Denial-of-Service attacks. According to \l {RFC 6347, section 4.2.1},
these are two of the more common types of attack:
\list
@@ -70,7 +35,7 @@
which can be quite large, thus flooding the victim machine with datagrams.
\endlist
- As a countermeasure to these attacks, \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1}
+ As a countermeasure to these attacks, \l {RFC 6347, section 4.2.1}
proposes a stateless cookie technique that a server may deploy:
\list
@@ -118,7 +83,7 @@
\note The default secret is shared by all objects of the classes QDtlsClientVerifier
and QDtls. Since this can impose security risks, RFC 6347 recommends to change
- the server's secret frequently. Please see \l {https://tools.ietf.org/html/rfc6347}{RFC 6347, section 4.2.1}
+ the server's secret frequently. Please see \l {RFC 6347, section 4.2.1}
for hints about possible server implementations. Cookie generator parameters
can be set using the class QDtlsClientVerifier::GeneratorParameters and
setCookieGeneratorParameters():
@@ -249,7 +214,7 @@
\warning It's recommended to call shutdown() before destroying the client's QDtls
object if you are planning to re-use the same port number to connect to the
server later. Otherwise, the server may drop incoming ClientHello messages,
- see \l{https://tools.ietf.org/html/rfc6347#page-25}{RFC 6347, section 4.2.8}
+ see \l {RFC 6347, section 4.2.8}
for more details and implementation hints.
If the server does not use QDtlsClientVerifier, it \e must configure its
@@ -278,8 +243,6 @@
/*!
\typedef QDtls::GeneratorParameters
-
- This is a synonym for QDtlsClientVerifier::GeneratorParameters.
*/
/*!
@@ -339,72 +302,6 @@
QT_BEGIN_NAMESPACE
-QSslConfiguration QDtlsBasePrivate::configuration() const
-{
- auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration);
- copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
- QSslConfiguration copy(copyPrivate);
- copyPrivate->sessionCipher = sessionCipher;
- copyPrivate->sessionProtocol = sessionProtocol;
-
- return copy;
-}
-
-void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration)
-{
- dtlsConfiguration.localCertificateChain = configuration.localCertificateChain();
- dtlsConfiguration.privateKey = configuration.privateKey();
- dtlsConfiguration.ciphers = configuration.ciphers();
- dtlsConfiguration.ellipticCurves = configuration.ellipticCurves();
- dtlsConfiguration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint();
- dtlsConfiguration.dhParams = configuration.diffieHellmanParameters();
- dtlsConfiguration.caCertificates = configuration.caCertificates();
- dtlsConfiguration.peerVerifyDepth = configuration.peerVerifyDepth();
- dtlsConfiguration.peerVerifyMode = configuration.peerVerifyMode();
- dtlsConfiguration.protocol = configuration.protocol();
- dtlsConfiguration.sslOptions = configuration.d->sslOptions;
- dtlsConfiguration.sslSession = configuration.sessionTicket();
- dtlsConfiguration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint();
- dtlsConfiguration.nextAllowedProtocols = configuration.allowedNextProtocols();
- dtlsConfiguration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol();
- dtlsConfiguration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus();
- dtlsConfiguration.dtlsCookieEnabled = configuration.dtlsCookieVerificationEnabled();
- dtlsConfiguration.allowRootCertOnDemandLoading = configuration.d->allowRootCertOnDemandLoading;
- dtlsConfiguration.backendConfig = configuration.backendConfiguration();
-
- clearDtlsError();
-}
-
-bool QDtlsBasePrivate::setCookieGeneratorParameters(QCryptographicHash::Algorithm alg,
- const QByteArray &key)
-{
- if (!key.size()) {
- setDtlsError(QDtlsError::InvalidInputParameters,
- QDtls::tr("Invalid (empty) secret"));
- return false;
- }
-
- clearDtlsError();
-
- hashAlgorithm = alg;
- secret = key;
-
- return true;
-}
-
-bool QDtlsBasePrivate::isDtlsProtocol(QSsl::SslProtocol protocol)
-{
- switch (protocol) {
- case QSsl::DtlsV1_0:
- case QSsl::DtlsV1_0OrLater:
- case QSsl::DtlsV1_2:
- case QSsl::DtlsV1_2OrLater:
- return true;
- default:
- return false;
- }
-}
-
static QString msgUnsupportedMulticastAddress()
{
return QDtls::tr("Multicast and broadcast addresses are not supported");
@@ -436,22 +333,37 @@ QDtlsClientVerifier::GeneratorParameters::GeneratorParameters(QCryptographicHash
{
}
+QDtlsClientVerifierPrivate::QDtlsClientVerifierPrivate()
+{
+ const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend is available, cannot verify DTLS client");
+ return;
+ }
+ backend.reset(tlsBackend->createDtlsCookieVerifier());
+ if (!backend.get())
+ qCWarning(lcSsl) << "The backend" << tlsBackend->backendName() << "does not support DTLS cookies";
+}
+
+QDtlsClientVerifierPrivate::~QDtlsClientVerifierPrivate() = default;
+
/*!
Constructs a QDtlsClientVerifier object, \a parent is passed to QObject's
constructor.
*/
QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent)
- : QObject(*new QDtlsClientVerifierOpenSSL, parent)
+ : QObject(*new QDtlsClientVerifierPrivate, parent)
{
Q_D(QDtlsClientVerifier);
- d->mode = QSslSocket::SslServerMode;
- // The default configuration suffices: verifier never does a full
- // handshake and upon verifying a cookie in a client hello message,
- // it reports success.
- auto conf = QSslConfiguration::defaultDtlsConfiguration();
- conf.setPeerVerifyMode(QSslSocket::VerifyNone);
- d->setConfiguration(conf);
+ if (auto *backend = d->backend.get()) {
+ // The default configuration suffices: verifier never does a full
+ // handshake and upon verifying a cookie in a client hello message,
+ // it reports success.
+ auto conf = QSslConfiguration::defaultDtlsConfiguration();
+ conf.setPeerVerifyMode(QSslSocket::VerifyNone);
+ backend->setConfiguration(conf);
+ }
}
/*!
@@ -475,8 +387,10 @@ QDtlsClientVerifier::~QDtlsClientVerifier()
bool QDtlsClientVerifier::setCookieGeneratorParameters(const GeneratorParameters &params)
{
Q_D(QDtlsClientVerifier);
+ if (auto *backend = d->backend.get())
+ return backend->setCookieGeneratorParameters(params);
- return d->setCookieGeneratorParameters(params.hash, params.secret);
+ return false;
}
/*!
@@ -493,7 +407,10 @@ QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorPar
{
Q_D(const QDtlsClientVerifier);
- return {d->hashAlgorithm, d->secret};
+ if (const auto *backend = d->backend.get())
+ return backend->cookieGeneratorParameters();
+
+ return {};
}
/*!
@@ -516,19 +433,23 @@ bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgr
{
Q_D(QDtlsClientVerifier);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket || address.isNull() || !dgram.size()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- tr("A valid UDP socket, non-empty datagram, valid address/port were expected"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ tr("A valid UDP socket, non-empty datagram, and valid address/port were expected"));
return false;
}
if (address.isBroadcast() || address.isMulticast()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- msgUnsupportedMulticastAddress());
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ msgUnsupportedMulticastAddress());
return false;
}
- return d->verifyClient(socket, dgram, address, port);
+ return backend->verifyClient(socket, dgram, address, port);
}
/*!
@@ -541,7 +462,10 @@ QByteArray QDtlsClientVerifier::verifiedHello() const
{
Q_D(const QDtlsClientVerifier);
- return d->verifiedClientHello;
+ if (const auto *backend = d->backend.get())
+ return backend->verifiedHello();
+
+ return {};
}
/*!
@@ -553,7 +477,10 @@ QDtlsError QDtlsClientVerifier::dtlsError() const
{
Q_D(const QDtlsClientVerifier);
- return d->errorCode;
+ if (const auto *backend = d->backend.get())
+ return backend->error();
+
+ return QDtlsError::TlsInitializationError;
}
/*!
@@ -563,11 +490,17 @@ QDtlsError QDtlsClientVerifier::dtlsError() const
*/
QString QDtlsClientVerifier::dtlsErrorString() const
{
- Q_D(const QDtlsBase);
+ Q_D(const QDtlsClientVerifier);
- return d->errorDescription;
+ if (const auto *backend = d->backend.get())
+ return backend->errorString();
+
+ return QStringLiteral("No TLS backend is available, no client verification");
}
+QDtlsPrivate::QDtlsPrivate() = default;
+QDtlsPrivate::~QDtlsPrivate() = default;
+
/*!
Creates a QDtls object, \a parent is passed to the QObject constructor.
\a mode is QSslSocket::SslServerMode for a server-side DTLS connection or
@@ -576,11 +509,19 @@ QString QDtlsClientVerifier::dtlsErrorString() const
\sa sslMode(), QSslSocket::SslMode
*/
QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent)
- : QObject(*new QDtlsPrivateOpenSSL, parent)
+ : QObject(*new QDtlsPrivate, parent)
{
Q_D(QDtls);
-
- d->mode = mode;
+ const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend found, QDtls is unsupported");
+ return;
+ }
+ d->backend.reset(tlsBackend->createDtlsCryptograph(this, mode));
+ if (!d->backend.get()) {
+ qCWarning(lcSsl) << "TLS backend" << tlsBackend->backendName()
+ << "does not support the protocol DTLS";
+ }
setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration());
}
@@ -603,29 +544,30 @@ bool QDtls::setPeer(const QHostAddress &address, quint16 port,
{
Q_D(QDtls);
- if (d->handshakeState != HandshakeNotStarted) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot set peer after handshake started"));
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
+ if (backend->state() != HandshakeNotStarted) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot set peer after handshake started"));
return false;
}
if (address.isNull()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- tr("Invalid address"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ tr("Invalid address"));
return false;
}
if (address.isBroadcast() || address.isMulticast()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- msgUnsupportedMulticastAddress());
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ msgUnsupportedMulticastAddress());
return false;
}
- d->clearDtlsError();
-
- d->remoteAddress = address;
- d->remotePort = port;
- d->peerVerificationName = verificationName;
+ backend->clearDtlsError();
+ backend->setPeer(address, port, verificationName);
return true;
}
@@ -642,14 +584,18 @@ bool QDtls::setPeerVerificationName(const QString &name)
{
Q_D(QDtls);
- if (d->handshakeState != HandshakeNotStarted) {
- d->setDtlsError(QDtlsError::InvalidOperation,
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
+ if (backend->state() != HandshakeNotStarted) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
tr("Cannot set verification name after handshake started"));
return false;
}
- d->clearDtlsError();
- d->peerVerificationName = name;
+ backend->clearDtlsError();
+ backend->setPeerVerificationName(name);
return true;
}
@@ -663,7 +609,10 @@ QHostAddress QDtls::peerAddress() const
{
Q_D(const QDtls);
- return d->remoteAddress;
+ if (const auto *backend = d->backend.get())
+ return backend->peerAddress();
+
+ return {};
}
/*!
@@ -673,9 +622,12 @@ QHostAddress QDtls::peerAddress() const
*/
quint16 QDtls::peerPort() const
{
- Q_D(const QDtlsBase);
+ Q_D(const QDtls);
+
+ if (const auto *backend = d->backend.get())
+ return backend->peerPort();
- return d->remotePort;
+ return 0;
}
/*!
@@ -688,7 +640,10 @@ QString QDtls::peerVerificationName() const
{
Q_D(const QDtls);
- return d->peerVerificationName;
+ if (const auto *backend = d->backend.get())
+ return backend->peerVerificationName();
+
+ return {};
}
/*!
@@ -701,7 +656,10 @@ QSslSocket::SslMode QDtls::sslMode() const
{
Q_D(const QDtls);
- return d->mode;
+ if (const auto *backend = d->backend.get())
+ return backend->cryptographMode();
+
+ return QSslSocket::UnencryptedMode;
}
/*!
@@ -714,7 +672,8 @@ void QDtls::setMtuHint(quint16 mtuHint)
{
Q_D(QDtls);
- d->mtuHint = mtuHint;
+ if (auto *backend = d->backend.get())
+ backend->setDtlsMtuHint(mtuHint);
}
/*!
@@ -726,7 +685,10 @@ quint16 QDtls::mtuHint() const
{
Q_D(const QDtls);
- return d->mtuHint;
+ if (const auto *backend = d->backend.get())
+ return backend->dtlsMtuHint();
+
+ return 0;
}
/*!
@@ -743,7 +705,10 @@ bool QDtls::setCookieGeneratorParameters(const GeneratorParameters &params)
{
Q_D(QDtls);
- return d->setCookieGeneratorParameters(params.hash, params.secret);
+ if (auto *backend = d->backend.get())
+ backend->setCookieGeneratorParameters(params);
+
+ return false;
}
/*!
@@ -761,7 +726,10 @@ QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const
{
Q_D(const QDtls);
- return {d->hashAlgorithm, d->secret};
+ if (const auto *backend = d->backend.get())
+ return backend->cookieGeneratorParameters();
+
+ return {};
}
/*!
@@ -776,13 +744,17 @@ bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration)
{
Q_D(QDtls);
- if (d->handshakeState != HandshakeNotStarted) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot set configuration after handshake started"));
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
+ if (backend->state() != HandshakeNotStarted) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot set configuration after handshake started"));
return false;
}
- d->setConfiguration(configuration);
+ backend->setConfiguration(configuration);
return true;
}
@@ -795,8 +767,10 @@ bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration)
QSslConfiguration QDtls::dtlsConfiguration() const
{
Q_D(const QDtls);
+ if (const auto *backend = d->backend.get())
+ return backend->configuration();
- return d->configuration();
+ return {};
}
/*!
@@ -808,7 +782,10 @@ QDtls::HandshakeState QDtls::handshakeState()const
{
Q_D(const QDtls);
- return d->handshakeState;
+ if (const auto *backend = d->backend.get())
+ return backend->state();
+
+ return QDtls::HandshakeNotStarted;
}
/*!
@@ -834,13 +811,17 @@ bool QDtls::doHandshake(QUdpSocket *socket, const QByteArray &dgram)
{
Q_D(QDtls);
- if (d->handshakeState == HandshakeNotStarted)
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
+ if (backend->state() == HandshakeNotStarted)
return startHandshake(socket, dgram);
- else if (d->handshakeState == HandshakeInProgress)
+ else if (backend->state() == HandshakeInProgress)
return continueHandshake(socket, dgram);
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot start/continue handshake, invalid handshake state"));
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot start/continue handshake, invalid handshake state"));
return false;
}
@@ -851,34 +832,38 @@ bool QDtls::startHandshake(QUdpSocket *socket, const QByteArray &datagram)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return false;
}
- if (d->remoteAddress.isNull()) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("To start a handshake you must set peer's address and port first"));
+ if (backend->peerAddress().isNull()) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("To start a handshake you must set peer's address and port first"));
return false;
}
if (sslMode() == QSslSocket::SslServerMode && !datagram.size()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- tr("To start a handshake, DTLS server requires non-empty datagram (client hello)"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ tr("To start a handshake, DTLS server requires non-empty datagram (client hello)"));
return false;
}
- if (d->handshakeState != HandshakeNotStarted) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot start handshake, already done/in progress"));
+ if (backend->state() != HandshakeNotStarted) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot start handshake, already done/in progress"));
return false;
}
- return d->startHandshake(socket, datagram);
+ return backend->startHandshake(socket, datagram);
}
/*!
- If a timeout occures during the handshake, the handshakeTimeout() signal
+ If a timeout occurs during the handshake, the handshakeTimeout() signal
is emitted. The application must call handleTimeout() to retransmit handshake
messages; handleTimeout() returns \c true if a timeout has occurred, false
otherwise. \a socket must be a valid pointer.
@@ -889,12 +874,16 @@ bool QDtls::handleTimeout(QUdpSocket *socket)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return false;
}
- return d->handleTimeout(socket);
+ return backend->handleTimeout(socket);
}
/*!
@@ -904,19 +893,23 @@ bool QDtls::continueHandshake(QUdpSocket *socket, const QByteArray &datagram)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket || !datagram.size()) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake"));
return false;
}
- if (d->handshakeState != HandshakeInProgress) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot continue handshake, not in InProgress state"));
+ if (backend->state() != HandshakeInProgress) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot continue handshake, not in InProgress state"));
return false;
}
- return d->continueHandshake(socket, datagram);
+ return backend->continueHandshake(socket, datagram);
}
/*!
@@ -931,18 +924,22 @@ bool QDtls::resumeHandshake(QUdpSocket *socket)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return false;
}
- if (d->handshakeState != PeerVerificationFailed) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot resume, not in VerificationError state"));
+ if (backend->state() != PeerVerificationFailed) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot resume, not in VerificationError state"));
return false;
}
- return d->resumeHandshake(socket);
+ return backend->resumeHandshake(socket);
}
/*!
@@ -955,18 +952,22 @@ bool QDtls::abortHandshake(QUdpSocket *socket)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return false;
}
- if (d->handshakeState != PeerVerificationFailed && d->handshakeState != HandshakeInProgress) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("No handshake in progress, nothing to abort"));
+ if (backend->state() != PeerVerificationFailed && backend->state() != HandshakeInProgress) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("No handshake in progress, nothing to abort"));
return false;
}
- d->abortHandshake(socket);
+ backend->abortHandshake(socket);
return true;
}
@@ -981,19 +982,23 @@ bool QDtls::shutdown(QUdpSocket *socket)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return false;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters,
- tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters,
+ tr("Invalid (nullptr) socket"));
return false;
}
- if (!d->connectionEncrypted) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot send shutdown alert, not encrypted"));
+ if (!backend->isConnectionEncrypted()) {
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot send shutdown alert, not encrypted"));
return false;
}
- d->sendShutdownAlert(socket);
+ backend->sendShutdownAlert(socket);
return true;
}
@@ -1006,7 +1011,11 @@ bool QDtls::isConnectionEncrypted() const
{
Q_D(const QDtls);
- return d->connectionEncrypted;
+
+ if (const auto *backend = d->backend.get())
+ return backend->isConnectionEncrypted();
+
+ return false;
}
/*!
@@ -1025,7 +1034,10 @@ QSslCipher QDtls::sessionCipher() const
{
Q_D(const QDtls);
- return d->sessionCipher;
+ if (const auto *backend = d->backend.get())
+ return backend->dtlsSessionCipher();
+
+ return {};
}
/*!
@@ -1042,7 +1054,10 @@ QSsl::SslProtocol QDtls::sessionProtocol() const
{
Q_D(const QDtls);
- return d->sessionProtocol;
+ if (const auto *backend = d->backend.get())
+ return backend->dtlsSessionProtocol();
+
+ return QSsl::UnknownProtocol;
}
/*!
@@ -1057,18 +1072,22 @@ qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return -1;
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return -1;
}
if (!isConnectionEncrypted()) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot write a datagram, not in encrypted state"));
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot write a datagram, not in encrypted state"));
return -1;
}
- return d->writeDatagramEncrypted(socket, dgram);
+ return backend->writeDatagramEncrypted(socket, dgram);
}
/*!
@@ -1081,21 +1100,25 @@ QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
{
Q_D(QDtls);
+ auto *backend = d->backend.get();
+ if (!backend)
+ return {};
+
if (!socket) {
- d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
+ backend->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket"));
return {};
}
if (!isConnectionEncrypted()) {
- d->setDtlsError(QDtlsError::InvalidOperation,
- tr("Cannot read a datagram, not in encrypted state"));
+ backend->setDtlsError(QDtlsError::InvalidOperation,
+ tr("Cannot read a datagram, not in encrypted state"));
return {};
}
if (!dgram.size())
return {};
- return d->decryptDatagram(socket, dgram);
+ return backend->decryptDatagram(socket, dgram);
}
/*!
@@ -1107,7 +1130,10 @@ QDtlsError QDtls::dtlsError() const
{
Q_D(const QDtls);
- return d->errorCode;
+ if (const auto *backend = d->backend.get())
+ return backend->error();
+
+ return QDtlsError::NoError;
}
/*!
@@ -1120,7 +1146,10 @@ QString QDtls::dtlsErrorString() const
{
Q_D(const QDtls);
- return d->errorDescription;
+ if (const auto *backend = d->backend.get())
+ return backend->errorString();
+
+ return {};
}
/*!
@@ -1129,11 +1158,15 @@ QString QDtls::dtlsErrorString() const
If you want to continue connecting despite the errors that have occurred,
you must call ignoreVerificationErrors().
*/
-QVector<QSslError> QDtls::peerVerificationErrors() const
+QList<QSslError> QDtls::peerVerificationErrors() const
{
Q_D(const QDtls);
- return d->tlsErrors;
+ if (const auto *backend = d->backend.get())
+ return backend->peerVerificationErrors();
+
+ //return d->tlsErrors;
+ return {};
}
/*!
@@ -1154,11 +1187,14 @@ QVector<QSslError> QDtls::peerVerificationErrors() const
\sa doHandshake(), resumeHandshake(), QSslError
*/
-void QDtls::ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore)
+void QDtls::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)
{
Q_D(QDtls);
- d->tlsErrorsToIgnore = errorsToIgnore;
+ if (auto *backend = d->backend.get())
+ backend->ignoreVerificationErrors(errorsToIgnore);
}
QT_END_NAMESPACE
+
+#include "moc_qdtls.cpp"
diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h
index d057eadf19..dd24aa219a 100644
--- a/src/network/ssl/qdtls.h
+++ b/src/network/ssl/qdtls.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDTLS_H
#define QDTLS_H
@@ -47,8 +11,11 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/qobject.h>
+#include <QtCore/qcontainerfwd.h>
-#ifndef Q_CLANG_QDOC
+Q_MOC_INCLUDE(<QtNetwork/QSslPreSharedKeyAuthenticator>)
+
+#ifndef Q_QDOC
QT_REQUIRE_CONFIG(dtls);
#endif
@@ -107,7 +74,6 @@ private:
};
class QSslPreSharedKeyAuthenticator;
-template<class> class QVector;
class QSslConfiguration;
class QSslCipher;
class QSslError;
@@ -166,8 +132,8 @@ public:
QDtlsError dtlsError() const;
QString dtlsErrorString() const;
- QVector<QSslError> peerVerificationErrors() const;
- void ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore);
+ QList<QSslError> peerVerificationErrors() const;
+ void ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore);
Q_SIGNALS:
@@ -180,7 +146,7 @@ private:
bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram);
Q_DECLARE_PRIVATE(QDtls)
- Q_DISABLE_COPY(QDtls)
+ Q_DISABLE_COPY_MOVE(QDtls)
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp
deleted file mode 100644
index 25a6c5f49c..0000000000
--- a/src/network/ssl/qdtls_openssl.cpp
+++ /dev/null
@@ -1,1401 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif // NOMINMAX
-#include "private/qnativesocketengine_p.h"
-
-#include "qsslpresharedkeyauthenticator_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslsocket_openssl_p.h"
-#include "qsslcertificate_p.h"
-#include "qdtls_openssl_p.h"
-#include "qudpsocket.h"
-#include "qssl_p.h"
-
-#include "qmessageauthenticationcode.h"
-#include "qcryptographichash.h"
-
-#include "qdebug.h"
-
-#include <cstring>
-#include <cstddef>
-
-QT_BEGIN_NAMESPACE
-
-#define QT_DTLS_VERBOSE 0
-
-#if QT_DTLS_VERBOSE
-
-#define qDtlsWarning(arg) qWarning(arg)
-#define qDtlsDebug(arg) qDebug(arg)
-
-#else
-
-#define qDtlsWarning(arg)
-#define qDtlsDebug(arg)
-
-#endif // QT_DTLS_VERBOSE
-
-namespace dtlsutil
-{
-
-QByteArray cookie_for_peer(SSL *ssl)
-{
- Q_ASSERT(ssl);
-
- // SSL_get_rbio does not increment the reference count
- BIO *readBIO = q_SSL_get_rbio(ssl);
- if (!readBIO) {
- qCWarning(lcSsl, "No BIO (dgram) found in SSL object");
- return {};
- }
-
- auto listener = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(readBIO));
- if (!listener) {
- qCWarning(lcSsl, "BIO_get_app_data returned invalid (nullptr) value");
- return {};
- }
-
- const QHostAddress peerAddress(listener->remoteAddress);
- const quint16 peerPort(listener->remotePort);
- QByteArray peerData;
- if (peerAddress.protocol() == QAbstractSocket::IPv6Protocol) {
- const Q_IPV6ADDR sin6_addr(peerAddress.toIPv6Address());
- peerData.resize(int(sizeof sin6_addr + sizeof peerPort));
- char *dst = peerData.data();
- std::memcpy(dst, &peerPort, sizeof peerPort);
- dst += sizeof peerPort;
- std::memcpy(dst, &sin6_addr, sizeof sin6_addr);
- } else if (peerAddress.protocol() == QAbstractSocket::IPv4Protocol) {
- const quint32 sin_addr(peerAddress.toIPv4Address());
- peerData.resize(int(sizeof sin_addr + sizeof peerPort));
- char *dst = peerData.data();
- std::memcpy(dst, &peerPort, sizeof peerPort);
- dst += sizeof peerPort;
- std::memcpy(dst, &sin_addr, sizeof sin_addr);
- } else {
- Q_UNREACHABLE();
- }
-
- return peerData;
-}
-
-struct FallbackCookieSecret
-{
- FallbackCookieSecret()
- {
- key.resize(32);
- const int status = q_RAND_bytes(reinterpret_cast<unsigned char *>(key.data()),
- key.size());
- if (status <= 0)
- key.clear();
- }
-
- QByteArray key;
-
- Q_DISABLE_COPY(FallbackCookieSecret)
-};
-
-QByteArray fallbackSecret()
-{
- static const FallbackCookieSecret generator;
- return generator.key;
-}
-
-int next_timeoutMs(SSL *tlsConnection)
-{
- Q_ASSERT(tlsConnection);
- timeval timeLeft = {};
- q_DTLSv1_get_timeout(tlsConnection, &timeLeft);
- return timeLeft.tv_sec * 1000;
-}
-
-
-void delete_connection(SSL *ssl)
-{
- // The 'deleter' for QSharedPointer<SSL>.
- if (ssl)
- q_SSL_free(ssl);
-}
-
-void delete_BIO_ADDR(BIO_ADDR *bio)
-{
- // A deleter for QSharedPointer<BIO_ADDR>
- if (bio)
- q_BIO_ADDR_free(bio);
-}
-
-void delete_bio_method(BIO_METHOD *method)
-{
- // The 'deleter' for QSharedPointer<BIO_METHOD>.
- if (method)
- q_BIO_meth_free(method);
-}
-
-// The 'deleter' for QScopedPointer<BIO>.
-struct bio_deleter
-{
- static void cleanup(BIO *bio)
- {
- if (bio)
- q_BIO_free(bio);
- }
-};
-
-// The path MTU discovery is non-trivial: it's a mix of getsockopt/setsockopt
-// (IP_MTU/IP6_MTU/IP_MTU_DISCOVER) and fallback MTU values. It's not
-// supported on all platforms, worse so - imposes specific requirements on
-// underlying UDP socket etc. So for now, we either try a user-proposed MTU
-// hint or rely on our own fallback value. As a fallback mtu OpenSSL uses 576
-// for IPv4 and 1280 for IPv6 (RFC 791, RFC 2460). To KIS we use 576. This
-// rather small MTU value does not affect the size that can be read/written
-// by QDtls, only a handshake (which is allowed to fragment).
-enum class MtuGuess : long
-{
- defaultMtu = 576
-};
-
-} // namespace dtlsutil
-
-namespace dtlscallbacks
-{
-
-extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
- unsigned *cookieLength)
-{
- if (!ssl || !dst || !cookieLength) {
- qCWarning(lcSsl,
- "Failed to generate cookie - invalid (nullptr) parameter(s)");
- return 0;
- }
-
- void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
- if (!generic) {
- qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, cannot generate cookie");
- return 0;
- }
-
- *cookieLength = 0;
-
- auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
- if (!dtls->secret.size())
- return 0;
-
- const QByteArray peerData(dtlsutil::cookie_for_peer(ssl));
- if (!peerData.size())
- return 0;
-
- QMessageAuthenticationCode hmac(dtls->hashAlgorithm, dtls->secret);
- hmac.addData(peerData);
- const QByteArray cookie = hmac.result();
- Q_ASSERT(cookie.size() >= 0);
- // DTLS1_COOKIE_LENGTH is erroneously 256 bytes long, must be 255 - RFC 6347, 4.2.1.
- *cookieLength = qMin(DTLS1_COOKIE_LENGTH - 1, cookie.size());
- std::memcpy(dst, cookie.constData(), *cookieLength);
-
- return 1;
-}
-
-extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
- unsigned cookieLength)
-{
- if (!ssl || !cookie || !cookieLength) {
- qCWarning(lcSsl, "Could not verify cookie, invalid (nullptr or zero) parameters");
- return 0;
- }
-
- unsigned char newCookie[DTLS1_COOKIE_LENGTH] = {};
- unsigned newCookieLength = 0;
- if (q_generate_cookie_callback(ssl, newCookie, &newCookieLength) != 1)
- return 0;
-
- return newCookieLength == cookieLength
- && !std::memcmp(cookie, newCookie, cookieLength);
-}
-
-extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx)
-{
- if (!ok) {
- // Store the error and at which depth the error was detected.
- SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()));
- if (!ssl) {
- qCWarning(lcSsl, "X509_STORE_CTX_get_ex_data returned nullptr, handshake failure");
- return 0;
- }
-
- void *generic = q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData);
- if (!generic) {
- qCWarning(lcSsl, "SSL_get_ex_data returned nullptr, handshake failure");
- return 0;
- }
-
- auto dtls = static_cast<dtlsopenssl::DtlsState *>(generic);
- dtls->x509Errors.append(QSslErrorEntry::fromStoreContext(ctx));
- }
-
- // Always return 1 (OK) to allow verification to continue. We handle the
- // errors gracefully after collecting all errors, after verification has
- // completed.
- return 1;
-}
-
-extern "C" unsigned q_PSK_client_callback(SSL *ssl, const char *hint, char *identity,
- unsigned max_identity_len, unsigned char *psk,
- unsigned max_psk_len)
-{
- auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
- QSslSocketBackendPrivate::s_indexForSSLExtraData));
- if (!dtls)
- return 0;
-
- Q_ASSERT(dtls->dtlsPrivate);
- return dtls->dtlsPrivate->pskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
-}
-
-extern "C" unsigned q_PSK_server_callback(SSL *ssl, const char *identity, unsigned char *psk,
- unsigned max_psk_len)
-{
- auto *dtls = static_cast<dtlsopenssl::DtlsState *>(q_SSL_get_ex_data(ssl,
- QSslSocketBackendPrivate::s_indexForSSLExtraData));
- if (!dtls)
- return 0;
-
- Q_ASSERT(dtls->dtlsPrivate);
- return dtls->dtlsPrivate->pskServerCallback(identity, psk, max_psk_len);
-}
-
-} // namespace dtlscallbacks
-
-namespace dtlsbio
-{
-
-extern "C" int q_dgram_read(BIO *bio, char *dst, int bytesToRead)
-{
- if (!bio || !dst || bytesToRead <= 0) {
- qCWarning(lcSsl, "invalid input parameter(s)");
- return 0;
- }
-
- q_BIO_clear_retry_flags(bio);
-
- auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
- // It's us who set data, if OpenSSL does too, the logic here is wrong
- // then and we have to use BIO_set_app_data then!
- Q_ASSERT(dtls);
- int bytesRead = 0;
- if (dtls->dgram.size()) {
- bytesRead = qMin(dtls->dgram.size(), bytesToRead);
- std::memcpy(dst, dtls->dgram.constData(), bytesRead);
-
- if (!dtls->peeking)
- dtls->dgram = dtls->dgram.mid(bytesRead);
- } else {
- bytesRead = -1;
- }
-
- if (bytesRead <= 0)
- q_BIO_set_retry_read(bio);
-
- return bytesRead;
-}
-
-extern "C" int q_dgram_write(BIO *bio, const char *src, int bytesToWrite)
-{
- if (!bio || !src || bytesToWrite <= 0) {
- qCWarning(lcSsl, "invalid input parameter(s)");
- return 0;
- }
-
- q_BIO_clear_retry_flags(bio);
-
- auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
- Q_ASSERT(dtls);
- if (dtls->writeSuppressed) {
- // See the comment in QDtls::startHandshake.
- return bytesToWrite;
- }
-
- QUdpSocket *udpSocket = dtls->udpSocket;
- Q_ASSERT(udpSocket);
-
- const QByteArray dgram(QByteArray::fromRawData(src, bytesToWrite));
- qint64 bytesWritten = -1;
- if (udpSocket->state() == QAbstractSocket::ConnectedState) {
- bytesWritten = udpSocket->write(dgram);
- } else {
- bytesWritten = udpSocket->writeDatagram(dgram, dtls->remoteAddress,
- dtls->remotePort);
- }
-
- if (bytesWritten <= 0)
- q_BIO_set_retry_write(bio);
-
- Q_ASSERT(bytesWritten <= std::numeric_limits<int>::max());
- return int(bytesWritten);
-}
-
-extern "C" int q_dgram_puts(BIO *bio, const char *src)
-{
- if (!bio || !src) {
- qCWarning(lcSsl, "invalid input parameter(s)");
- return 0;
- }
-
- return q_dgram_write(bio, src, int(std::strlen(src)));
-}
-
-extern "C" long q_dgram_ctrl(BIO *bio, int cmd, long num, void *ptr)
-{
- // This is our custom BIO_ctrl. bio.h defines a lot of BIO_CTRL_*
- // and BIO_* constants and BIO_somename macros that expands to BIO_ctrl
- // call with one of those constants as argument. What exactly BIO_ctrl
- // does - depends on the 'cmd' and the type of BIO (so BIO_ctrl does
- // not even have a single well-defined value meaning success or failure).
- // We handle only the most generic commands - the ones documented for
- // BIO_ctrl - and also DGRAM specific ones. And even for them - in most
- // cases we do nothing but report a success or some non-error value.
- // Documents also state: "Source/sink BIOs return an 0 if they do not
- // recognize the BIO_ctrl() operation." - these are covered by 'default'
- // label in the switch-statement below. Debug messages in the switch mean:
- // 1) we got a command that is unexpected for dgram BIO, or:
- // 2) we do not call any function that would lead to OpenSSL using this
- // command.
-
- if (!bio) {
- qDebug(lcSsl, "invalid 'bio' parameter (nullptr)");
- return -1;
- }
-
- auto dtls = static_cast<dtlsopenssl::DtlsState *>(q_BIO_get_app_data(bio));
- Q_ASSERT(dtls);
-
- switch (cmd) {
- // Let's start from the most generic ones, in the order in which they are
- // documented (as BIO_ctrl):
- case BIO_CTRL_RESET:
- // BIO_reset macro.
- // From documentation:
- // "BIO_reset() normally returns 1 for success and 0 or -1 for failure.
- // File BIOs are an exception, they return 0 for success and -1 for
- // failure."
- // We have nothing to reset and we are not file BIO.
- return 1;
- case BIO_C_FILE_SEEK:
- case BIO_C_FILE_TELL:
- qDtlsWarning("Unexpected cmd (BIO_C_FILE_SEEK/BIO_C_FILE_TELL)");
- // These are for BIO_seek, BIO_tell. We are not a file BIO.
- // Non-negative return value means success.
- return 0;
- case BIO_CTRL_FLUSH:
- // BIO_flush, nothing to do, we do not buffer any data.
- // 0 or -1 means error, 1 - success.
- return 1;
- case BIO_CTRL_EOF:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_EOF)");
- // BIO_eof, 1 means EOF read. Makes no sense for us.
- return 0;
- case BIO_CTRL_SET_CLOSE:
- // BIO_set_close with BIO_CLOSE/BIO_NOCLOSE flags. Documented as
- // always returning 1.
- // From the documentation:
- // "Typically BIO_CLOSE is used in a source/sink BIO to indicate that
- // the underlying I/O stream should be closed when the BIO is freed."
- //
- // QUdpSocket we work with is not BIO's business, ignoring.
- return 1;
- case BIO_CTRL_GET_CLOSE:
- // BIO_get_close. No, never, see the comment above.
- return 0;
- case BIO_CTRL_PENDING:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_PENDING)");
- // BIO_pending. Not used by DTLS/OpenSSL (we are not buffering).
- return 0;
- case BIO_CTRL_WPENDING:
- // No, we have nothing buffered.
- return 0;
- // The constants below are not documented as a part BIO_ctrl documentation,
- // but they are also not type-specific.
- case BIO_CTRL_DUP:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DUP)");
- // BIO_dup_state, not used by DTLS (and socket-related BIOs in general).
- // For some very specific BIO type this 'cmd' would copy some state
- // from 'bio' to (BIO*)'ptr'. 1 means success.
- return 0;
- case BIO_CTRL_SET_CALLBACK:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_SET_CALLBACK)");
- // BIO_set_info_callback. We never call this, OpenSSL does not do this
- // on its own (normally it's used if client code wants to have some
- // debug information, for example, dumping handshake state via
- // BIO_printf from SSL info_callback).
- return 0;
- case BIO_CTRL_GET_CALLBACK:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_GET_CALLBACK)");
- // BIO_get_info_callback. We never call this.
- if (ptr)
- *static_cast<bio_info_cb **>(ptr) = nullptr;
- return 0;
- case BIO_CTRL_SET:
- case BIO_CTRL_GET:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_SET/BIO_CTRL_GET)");
- // Somewhat 'documented' as setting/getting IO type. Not used anywhere
- // except BIO_buffer_get_num_lines (which contradics 'get IO type').
- // Ignoring.
- return 0;
- // DGRAM-specific operation, we have to return some reasonable value
- // (so far, I've encountered only peek mode switching, connect).
- case BIO_CTRL_DGRAM_CONNECT:
- // BIO_ctrl_dgram_connect. Not needed. Our 'dtls' already knows
- // the peer's address/port. Report success though.
- return 1;
- case BIO_CTRL_DGRAM_SET_CONNECTED:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_CONNECTED)");
- // BIO_ctrl_dgram_set_connected. We never call it, OpenSSL does
- // not call it on its own (so normally it's done by client code).
- // Similar to BIO_CTRL_DGRAM_CONNECT, but it also informs the BIO
- // that its UDP socket is connected. We never need it though.
- return -1;
- case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_RECV_TIMEOUT)");
- // Essentially setsockopt with SO_RCVTIMEO, not needed, our sockets
- // are non-blocking.
- return -1;
- case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_RECV_TIMEOUT)");
- // getsockopt with SO_RCVTIMEO, not needed, our sockets are
- // non-blocking. ptr is timeval *.
- return -1;
- case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_SEND_TIMEOUT)");
- // setsockopt, SO_SNDTIMEO, cannot happen.
- return -1;
- case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_SEND_TIMEOUT)");
- // getsockopt, SO_SNDTIMEO, cannot happen.
- return -1;
- case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
- // BIO_dgram_recv_timedout. No, we are non-blocking.
- return 0;
- case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
- // BIO_dgram_send_timedout. No, we are non-blocking.
- return 0;
- case BIO_CTRL_DGRAM_MTU_DISCOVER:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_DISCOVER)");
- // setsockopt, IP_MTU_DISCOVER/IP6_MTU_DISCOVER, to be done
- // in QUdpSocket instead. OpenSSL never calls it, only client
- // code.
- return 1;
- case BIO_CTRL_DGRAM_QUERY_MTU:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_QUERY_MTU)");
- // To be done in QUdpSocket instead.
- return 1;
- case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
- qDtlsWarning("Unexpected command *BIO_CTRL_DGRAM_GET_FALLBACK_MTU)");
- // Without SSL_OP_NO_QUERY_MTU set on SSL, OpenSSL can request for
- // fallback MTU after several re-transmissions.
- // Should never happen in our case.
- return long(dtlsutil::MtuGuess::defaultMtu);
- case BIO_CTRL_DGRAM_GET_MTU:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_GET_MTU)");
- return -1;
- case BIO_CTRL_DGRAM_SET_MTU:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_SET_MTU)");
- // Should not happen (we don't call BIO_ctrl with this parameter)
- // and set MTU on SSL instead.
- return -1; // num is mtu and it's a return value meaning success.
- case BIO_CTRL_DGRAM_MTU_EXCEEDED:
- qDtlsWarning("Unexpected cmd (BIO_CTRL_DGRAM_MTU_EXCEEDED)");
- return 0;
- case BIO_CTRL_DGRAM_GET_PEER:
- qDtlsDebug("BIO_CTRL_DGRAM_GET_PEER");
- // BIO_dgram_get_peer. We do not return a real address (DTLS is not
- // using this address), but let's pretend a success.
- switch (dtls->remoteAddress.protocol()) {
- case QAbstractSocket::IPv6Protocol:
- return sizeof(sockaddr_in6);
- case QAbstractSocket::IPv4Protocol:
- return sizeof(sockaddr_in);
- default:
- return -1;
- }
- case BIO_CTRL_DGRAM_SET_PEER:
- // Similar to BIO_CTRL_DGRAM_CONNECTED.
- return 1;
- case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
- // DTLSTODO: I'm not sure yet, how it's used by OpenSSL.
- return 1;
- case BIO_CTRL_DGRAM_SET_DONT_FRAG:
- qDtlsDebug("BIO_CTRL_DGRAM_SET_DONT_FRAG");
- // To be done in QUdpSocket, it's about IP_DONTFRAG etc.
- return 1;
- case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
- // AFAIK it's 28 for IPv4 and 48 for IPv6, but let's pretend it's 0
- // so that OpenSSL does not start suddenly fragmenting the first
- // client hello (which will result in DTLSv1_listen rejecting it).
- return 0;
- case BIO_CTRL_DGRAM_SET_PEEK_MODE:
- dtls->peeking = num;
- return 1;
- default:;
-#if QT_DTLS_VERBOSE
- qWarning() << "Unexpected cmd (" << cmd << ")";
-#endif
- }
-
- return 0;
-}
-
-extern "C" int q_dgram_create(BIO *bio)
-{
-
- q_BIO_set_init(bio, 1);
- // With a custom BIO you'd normally allocate some implementation-specific
- // data and append it to this new BIO using BIO_set_data. We don't need
- // it and thus q_dgram_destroy below is a noop.
- return 1;
-}
-
-extern "C" int q_dgram_destroy(BIO *bio)
-{
- Q_UNUSED(bio)
- return 1;
-}
-
-const char * const qdtlsMethodName = "qdtlsbio";
-
-} // namespace dtlsbio
-
-namespace dtlsopenssl
-{
-
-bool DtlsState::init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
- const QHostAddress &remote, quint16 port,
- const QByteArray &receivedMessage)
-{
- Q_ASSERT(dtlsBase);
- Q_ASSERT(socket);
-
- if (!tlsContext.data() && !initTls(dtlsBase))
- return false;
-
- udpSocket = socket;
-
- setLinkMtu(dtlsBase);
-
- dgram = receivedMessage;
- remoteAddress = remote;
- remotePort = port;
-
- // SSL_get_rbio does not increment a reference count.
- BIO *bio = q_SSL_get_rbio(tlsConnection.data());
- Q_ASSERT(bio);
- q_BIO_set_app_data(bio, this);
-
- return true;
-}
-
-void DtlsState::reset()
-{
- tlsConnection.reset();
- tlsContext.reset();
-}
-
-bool DtlsState::initTls(QDtlsBasePrivate *dtlsBase)
-{
- if (tlsContext.data())
- return true;
-
- if (!QSslSocket::supportsSsl())
- return false;
-
- if (!initCtxAndConnection(dtlsBase))
- return false;
-
- if (!initBIO(dtlsBase)) {
- tlsConnection.reset();
- tlsContext.reset();
- return false;
- }
-
- return true;
-}
-
-static QString msgFunctionFailed(const char *function)
-{
- //: %1: Some function
- return QDtls::tr("%1 failed").arg(QLatin1String(function));
-}
-
-bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase)
-{
- Q_ASSERT(dtlsBase);
- Q_ASSERT(QSslSocket::supportsSsl());
-
- if (dtlsBase->mode == QSslSocket::UnencryptedMode) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- QDtls::tr("Invalid SslMode, SslServerMode or SslClientMode expected"));
- return false;
- }
-
- if (!QDtlsBasePrivate::isDtlsProtocol(dtlsBase->dtlsConfiguration.protocol)) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- QDtls::tr("Invalid protocol version, DTLS protocol expected"));
- return false;
- }
-
- // Create a deep copy of our configuration
- auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration);
- configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
-
- // DTLSTODO: check we do not set something DTLS-incompatible there ...
- TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode,
- configurationCopy,
- dtlsBase->dtlsConfiguration.allowRootCertOnDemandLoading));
-
- if (newContext->error() != QSslError::NoError) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError, newContext->errorString());
- return false;
- }
-
- TlsConnection newConnection(newContext->createSsl(), dtlsutil::delete_connection);
- if (!newConnection.data()) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- msgFunctionFailed("SSL_new"));
- return false;
- }
-
- const int set = q_SSL_set_ex_data(newConnection.data(),
- QSslSocketBackendPrivate::s_indexForSSLExtraData,
- this);
-
- if (set != 1 && configurationCopy->peerVerifyMode != QSslSocket::VerifyNone) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- msgFunctionFailed("SSL_set_ex_data"));
- return false;
- }
-
- if (dtlsBase->mode == QSslSocket::SslServerMode) {
- if (dtlsBase->dtlsConfiguration.dtlsCookieEnabled)
- q_SSL_set_options(newConnection.data(), SSL_OP_COOKIE_EXCHANGE);
- q_SSL_set_psk_server_callback(newConnection.data(), dtlscallbacks::q_PSK_server_callback);
- } else {
- q_SSL_set_psk_client_callback(newConnection.data(), dtlscallbacks::q_PSK_client_callback);
- }
-
- tlsContext.swap(newContext);
- tlsConnection.swap(newConnection);
-
- return true;
-}
-
-bool DtlsState::initBIO(QDtlsBasePrivate *dtlsBase)
-{
- Q_ASSERT(dtlsBase);
- Q_ASSERT(tlsContext.data() && tlsConnection.data());
-
- BioMethod customMethod(q_BIO_meth_new(BIO_TYPE_DGRAM, dtlsbio::qdtlsMethodName),
- dtlsutil::delete_bio_method);
- if (!customMethod.data()) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- msgFunctionFailed("BIO_meth_new"));
- return false;
- }
-
- BIO_METHOD *biom = customMethod.data();
- q_BIO_meth_set_create(biom, dtlsbio::q_dgram_create);
- q_BIO_meth_set_destroy(biom, dtlsbio::q_dgram_destroy);
- q_BIO_meth_set_read(biom, dtlsbio::q_dgram_read);
- q_BIO_meth_set_write(biom, dtlsbio::q_dgram_write);
- q_BIO_meth_set_puts(biom, dtlsbio::q_dgram_puts);
- q_BIO_meth_set_ctrl(biom, dtlsbio::q_dgram_ctrl);
-
- QScopedPointer<BIO, dtlsutil::bio_deleter> newBio(q_BIO_new(biom));
- BIO *bio = newBio.data();
- if (!bio) {
- dtlsBase->setDtlsError(QDtlsError::TlsInitializationError,
- msgFunctionFailed("BIO_new"));
- return false;
- }
-
- q_SSL_set_bio(tlsConnection.data(), bio, bio);
- newBio.take();
-
- bioMethod.swap(customMethod);
-
- return true;
-}
-
-void DtlsState::setLinkMtu(QDtlsBasePrivate *dtlsBase)
-{
- Q_ASSERT(dtlsBase);
- Q_ASSERT(udpSocket);
- Q_ASSERT(tlsConnection.data());
-
- long mtu = dtlsBase->mtuHint;
- if (!mtu) {
- // If the underlying QUdpSocket was connected, getsockopt with
- // IP_MTU/IP6_MTU can give us some hint:
- bool optionFound = false;
- if (udpSocket->state() == QAbstractSocket::ConnectedState) {
- const QVariant val(udpSocket->socketOption(QAbstractSocket::PathMtuSocketOption));
- if (val.isValid() && val.canConvert<int>())
- mtu = val.toInt(&optionFound);
- }
-
- if (!optionFound || mtu <= 0) {
- // OK, our own initial guess.
- mtu = long(dtlsutil::MtuGuess::defaultMtu);
- }
- }
-
- // For now, we disable this option.
- q_SSL_set_options(tlsConnection.data(), SSL_OP_NO_QUERY_MTU);
-
- q_DTLS_set_link_mtu(tlsConnection.data(), mtu);
-}
-
-} // namespace dtlsopenssl
-
-QDtlsClientVerifierOpenSSL::QDtlsClientVerifierOpenSSL()
-{
- secret = dtlsutil::fallbackSecret();
-}
-
-bool QDtlsClientVerifierOpenSSL::verifyClient(QUdpSocket *socket, const QByteArray &dgram,
- const QHostAddress &address, quint16 port)
-{
- Q_ASSERT(socket);
- Q_ASSERT(dgram.size());
- Q_ASSERT(!address.isNull());
- Q_ASSERT(port);
-
- clearDtlsError();
- verifiedClientHello.clear();
-
- if (!dtls.init(this, socket, address, port, dgram))
- return false;
-
- dtls.secret = secret;
- dtls.hashAlgorithm = hashAlgorithm;
-
- Q_ASSERT(dtls.tlsConnection.data());
- QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
- if (!peer.data()) {
- setDtlsError(QDtlsError::TlsInitializationError,
- QDtlsClientVerifier::tr("BIO_ADDR_new failed, ignoring client hello"));
- return false;
- }
-
- const int ret = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
- if (ret < 0) {
- // Since 1.1 - it's a fatal error (not so in 1.0.2 for non-blocking socket)
- setDtlsError(QDtlsError::TlsFatalError, QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- return false;
- }
-
- if (ret > 0) {
- verifiedClientHello = dgram;
- return true;
- }
-
- return false;
-}
-
-void QDtlsPrivateOpenSSL::TimeoutHandler::start(int hintMs)
-{
- Q_ASSERT(timerId == -1);
- timerId = startTimer(hintMs > 0 ? hintMs : timeoutMs, Qt::PreciseTimer);
-}
-
-void QDtlsPrivateOpenSSL::TimeoutHandler::doubleTimeout()
-{
- if (timeoutMs * 2 < 60000)
- timeoutMs *= 2;
- else
- timeoutMs = 60000;
-}
-
-void QDtlsPrivateOpenSSL::TimeoutHandler::stop()
-{
- if (timerId != -1) {
- killTimer(timerId);
- timerId = -1;
- }
-}
-
-void QDtlsPrivateOpenSSL::TimeoutHandler::timerEvent(QTimerEvent *event)
-{
- Q_UNUSED(event)
- Q_ASSERT(timerId != -1);
-
- killTimer(timerId);
- timerId = -1;
-
- Q_ASSERT(dtlsConnection);
- dtlsConnection->reportTimeout();
-}
-
-QDtlsPrivateOpenSSL::QDtlsPrivateOpenSSL()
-{
- secret = dtlsutil::fallbackSecret();
- dtls.dtlsPrivate = this;
-}
-
-bool QDtlsPrivateOpenSSL::startHandshake(QUdpSocket *socket, const QByteArray &dgram)
-{
- Q_ASSERT(socket);
- Q_ASSERT(handshakeState == QDtls::HandshakeNotStarted);
-
- clearDtlsError();
- connectionEncrypted = false;
-
- if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
- return false;
-
- if (mode == QSslSocket::SslServerMode && dtlsConfiguration.dtlsCookieEnabled) {
- dtls.secret = secret;
- dtls.hashAlgorithm = hashAlgorithm;
- // Let's prepare the state machine so that message sequence 1 does not
- // surprise DTLS/OpenSSL (such a message would be disregarded as
- // 'stale or future' in SSL_accept otherwise):
- int result = 0;
- QSharedPointer<BIO_ADDR> peer(q_BIO_ADDR_new(), dtlsutil::delete_BIO_ADDR);
- if (!peer.data()) {
- setDtlsError(QDtlsError::TlsInitializationError,
- QDtls::tr("BIO_ADD_new failed, cannot start handshake"));
- return false;
- }
-
- // If it's an invalid/unexpected ClientHello, we don't want to send
- // VerifyClientRequest - it's a job of QDtlsClientVerifier - so we
- // suppress any attempts to write into socket:
- dtls.writeSuppressed = true;
- result = q_DTLSv1_listen(dtls.tlsConnection.data(), peer.data());
- dtls.writeSuppressed = false;
-
- if (result <= 0) {
- setDtlsError(QDtlsError::TlsFatalError,
- QDtls::tr("Cannot start the handshake, verified client hello expected"));
- dtls.reset();
- return false;
- }
- }
-
- handshakeState = QDtls::HandshakeInProgress;
- opensslErrors.clear();
- tlsErrors.clear();
-
- return continueHandshake(socket, dgram);
-}
-
-bool QDtlsPrivateOpenSSL::continueHandshake(QUdpSocket *socket, const QByteArray &dgram)
-{
- Q_ASSERT(socket);
-
- Q_ASSERT(handshakeState == QDtls::HandshakeInProgress);
-
- clearDtlsError();
-
- if (timeoutHandler.data())
- timeoutHandler->stop();
-
- if (!dtls.init(this, socket, remoteAddress, remotePort, dgram))
- return false;
-
- dtls.x509Errors.clear();
-
- int result = 0;
- if (mode == QSslSocket::SslServerMode)
- result = q_SSL_accept(dtls.tlsConnection.data());
- else
- result = q_SSL_connect(dtls.tlsConnection.data());
-
- // DTLSTODO: Investigate/test if it makes sense - QSslSocket can emit
- // peerVerifyError at this point (and thus potentially client code
- // will close the underlying TCP connection immediately), but we are using
- // QUdpSocket, no connection to close, our verification callback returns 1
- // (verified OK) and this probably means OpenSSL has already sent a reply
- // to the server's hello/certificate.
-
- opensslErrors << dtls.x509Errors;
-
- if (result <= 0) {
- const auto code = q_SSL_get_error(dtls.tlsConnection.data(), result);
- switch (code) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- // DTLSTODO: to be tested - in principle, if it was the first call to
- // continueHandshake and server for some reason discards the client
- // hello message (even the verified one) - our 'this' will probably
- // forever stay in this strange InProgress state? (the client
- // will dully re-transmit the same hello and we discard it again?)
- // SSL_get_state can provide more information about state
- // machine and we can switch to NotStarted (since we have not
- // replied with our hello ...)
- if (!timeoutHandler.data()) {
- timeoutHandler.reset(new TimeoutHandler);
- timeoutHandler->dtlsConnection = this;
- } else {
- // Back to 1s.
- timeoutHandler->resetTimeout();
- }
-
- timeoutHandler->start();
-
- return true; // The handshake is not yet complete.
- default:
- storePeerCertificates();
- setDtlsError(QDtlsError::TlsFatalError,
- QSslSocketBackendPrivate::msgErrorsDuringHandshake());
- dtls.reset();
- handshakeState = QDtls::HandshakeNotStarted;
- return false;
- }
- }
-
- storePeerCertificates();
- fetchNegotiatedParameters();
-
- const bool doVerifyPeer = dtlsConfiguration.peerVerifyMode == QSslSocket::VerifyPeer
- || (dtlsConfiguration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
-
- if (!doVerifyPeer || verifyPeer() || tlsErrorsWereIgnored()) {
- connectionEncrypted = true;
- handshakeState = QDtls::HandshakeComplete;
- return true;
- }
-
- setDtlsError(QDtlsError::PeerVerificationError, QDtls::tr("Peer verification failed"));
- handshakeState = QDtls::PeerVerificationFailed;
- return false;
-}
-
-
-bool QDtlsPrivateOpenSSL::handleTimeout(QUdpSocket *socket)
-{
- Q_ASSERT(socket);
-
- Q_ASSERT(timeoutHandler.data());
- Q_ASSERT(dtls.tlsConnection.data());
-
- clearDtlsError();
-
- dtls.udpSocket = socket;
-
- if (q_DTLSv1_handle_timeout(dtls.tlsConnection.data()) > 0) {
- timeoutHandler->doubleTimeout();
- timeoutHandler->start();
- } else {
- timeoutHandler->start(dtlsutil::next_timeoutMs(dtls.tlsConnection.data()));
- }
-
- return true;
-}
-
-bool QDtlsPrivateOpenSSL::resumeHandshake(QUdpSocket *socket)
-{
- Q_UNUSED(socket);
- Q_ASSERT(socket);
- Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed);
-
- clearDtlsError();
-
- if (tlsErrorsWereIgnored()) {
- handshakeState = QDtls::HandshakeComplete;
- connectionEncrypted = true;
- tlsErrors.clear();
- tlsErrorsToIgnore.clear();
- return true;
- }
-
- return false;
-}
-
-void QDtlsPrivateOpenSSL::abortHandshake(QUdpSocket *socket)
-{
- Q_ASSERT(socket);
- Q_ASSERT(handshakeState == QDtls::PeerVerificationFailed
- || handshakeState == QDtls::HandshakeInProgress);
-
- clearDtlsError();
-
- if (handshakeState == QDtls::PeerVerificationFailed) {
- // Yes, while peer verification failed, we were actually encrypted.
- // Let's play it nice - inform our peer about connection shut down.
- sendShutdownAlert(socket);
- } else {
- resetDtls();
- }
-}
-
-void QDtlsPrivateOpenSSL::sendShutdownAlert(QUdpSocket *socket)
-{
- Q_ASSERT(socket);
-
- clearDtlsError();
-
- if (connectionEncrypted && !connectionWasShutdown) {
- dtls.udpSocket = socket;
- Q_ASSERT(dtls.tlsConnection.data());
- q_SSL_shutdown(dtls.tlsConnection.data());
- }
-
- resetDtls();
-}
-
-qint64 QDtlsPrivateOpenSSL::writeDatagramEncrypted(QUdpSocket *socket,
- const QByteArray &dgram)
-{
- Q_ASSERT(socket);
- Q_ASSERT(dtls.tlsConnection.data());
- Q_ASSERT(connectionEncrypted);
-
- clearDtlsError();
-
- dtls.udpSocket = socket;
- const int written = q_SSL_write(dtls.tlsConnection.data(),
- dgram.constData(), dgram.size());
- if (written > 0)
- return written;
-
- const unsigned long errorCode = q_ERR_get_error();
- if (!dgram.size() && errorCode == SSL_ERROR_NONE) {
- // With OpenSSL <= 1.1 this can happen. For example, DTLS client
- // tries to reconnect (while re-using the same address/port) -
- // DTLS server drops a message with unexpected epoch but says - no
- // error. We leave to client code to resolve such problems until
- // OpenSSL provides something better.
- return 0;
- }
-
- switch (errorCode) {
- case SSL_ERROR_WANT_WRITE:
- case SSL_ERROR_WANT_READ:
- // We do not set any error/description ... a user can probably re-try
- // sending a datagram.
- break;
- case SSL_ERROR_ZERO_RETURN:
- connectionWasShutdown = true;
- setDtlsError(QDtlsError::TlsFatalError, QDtls::tr("The DTLS connection has been closed"));
- handshakeState = QDtls::HandshakeNotStarted;
- dtls.reset();
- break;
- case SSL_ERROR_SYSCALL:
- case SSL_ERROR_SSL:
- default:
- // DTLSTODO: we don't know yet what to do. Tests needed - probably,
- // some errors can be just ignored (it's UDP, not TCP after all).
- // Unlike QSslSocket we do not abort though.
- QString description(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- if (socket->error() != QAbstractSocket::UnknownSocketError && description.isEmpty()) {
- setDtlsError(QDtlsError::UnderlyingSocketError, socket->errorString());
- } else {
- setDtlsError(QDtlsError::TlsFatalError,
- QDtls::tr("Error while writing: %1").arg(description));
- }
- }
-
- return -1;
-}
-
-QByteArray QDtlsPrivateOpenSSL::decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram)
-{
- Q_ASSERT(socket);
- Q_ASSERT(tlsdgram.size());
-
- Q_ASSERT(dtls.tlsConnection.data());
- Q_ASSERT(connectionEncrypted);
-
- dtls.dgram = tlsdgram;
- dtls.udpSocket = socket;
-
- clearDtlsError();
-
- QByteArray dgram;
- dgram.resize(tlsdgram.size());
- const int read = q_SSL_read(dtls.tlsConnection.data(), dgram.data(),
- dgram.size());
-
- if (read > 0) {
- dgram.resize(read);
- return dgram;
- }
-
- dgram.clear();
- unsigned long errorCode = q_ERR_get_error();
- if (errorCode == SSL_ERROR_NONE) {
- const int shutdown = q_SSL_get_shutdown(dtls.tlsConnection.data());
- if (shutdown & SSL_RECEIVED_SHUTDOWN)
- errorCode = SSL_ERROR_ZERO_RETURN;
- else
- return dgram;
- }
-
- switch (errorCode) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return dgram;
- case SSL_ERROR_ZERO_RETURN:
- // "The connection was shut down cleanly" ... hmm, whatever,
- // needs testing (DTLSTODO).
- connectionWasShutdown = true;
- setDtlsError(QDtlsError::RemoteClosedConnectionError,
- QDtls::tr("The DTLS connection has been shutdown"));
- dtls.reset();
- connectionEncrypted = false;
- handshakeState = QDtls::HandshakeNotStarted;
- return dgram;
- case SSL_ERROR_SYSCALL: // some IO error
- case SSL_ERROR_SSL: // error in the SSL library
- // DTLSTODO: Apparently, some errors can be ignored, for example,
- // ECONNRESET etc. This all needs a lot of testing!!!
- default:
- setDtlsError(QDtlsError::TlsNonFatalError,
- QDtls::tr("Error while reading: %1")
- .arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl()));
- return dgram;
- }
-}
-
-unsigned QDtlsPrivateOpenSSL::pskClientCallback(const char *hint, char *identity,
- unsigned max_identity_len,
- unsigned char *psk,
- unsigned max_psk_len)
-{
- // The code below is taken (with some modifications) from qsslsocket_openssl
- // - alas, we cannot simply re-use it, it's in QSslSocketPrivate.
-
- Q_Q(QDtls);
-
- {
- QSslPreSharedKeyAuthenticator authenticator;
- // Fill in some read-only fields (for client code)
- if (hint) {
- identityHint.clear();
- identityHint.append(hint);
- // From the original code in QSslSocket:
- // "it's NULL terminated, but do not include the NULL" == this fromRawData(ptr/size).
- authenticator.d->identityHint = QByteArray::fromRawData(identityHint.constData(),
- int(std::strlen(hint)));
- }
-
- authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NULL terminated
- authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
-
- pskAuthenticator.swap(authenticator);
- }
-
- // Let the client provide the remaining bits...
- emit q->pskRequired(&pskAuthenticator);
-
- // No PSK set? Return now to make the handshake fail
- if (pskAuthenticator.preSharedKey().isEmpty())
- return 0;
-
- // Copy data back into OpenSSL
- const int identityLength = qMin(pskAuthenticator.identity().length(),
- pskAuthenticator.maximumIdentityLength());
- std::memcpy(identity, pskAuthenticator.identity().constData(), identityLength);
- identity[identityLength] = 0;
-
- const int pskLength = qMin(pskAuthenticator.preSharedKey().length(),
- pskAuthenticator.maximumPreSharedKeyLength());
- std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
-
- return pskLength;
-}
-
-unsigned QDtlsPrivateOpenSSL::pskServerCallback(const char *identity, unsigned char *psk,
- unsigned max_psk_len)
-{
- Q_Q(QDtls);
-
- {
- QSslPreSharedKeyAuthenticator authenticator;
- // Fill in some read-only fields (for the user)
- authenticator.d->identityHint = dtlsConfiguration.preSharedKeyIdentityHint;
- authenticator.d->identity = identity;
- authenticator.d->maximumIdentityLength = 0; // user cannot set an identity
- authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
-
- pskAuthenticator.swap(authenticator);
- }
-
- // Let the client provide the remaining bits...
- emit q->pskRequired(&pskAuthenticator);
-
- // No PSK set? Return now to make the handshake fail
- if (pskAuthenticator.preSharedKey().isEmpty())
- return 0;
-
- // Copy data back into OpenSSL
- const int pskLength = qMin(pskAuthenticator.preSharedKey().length(),
- pskAuthenticator.maximumPreSharedKeyLength());
-
- std::memcpy(psk, pskAuthenticator.preSharedKey().constData(), pskLength);
-
- return pskLength;
-}
-
-// The definition is located in qsslsocket_openssl.cpp.
-QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert);
-
-bool QDtlsPrivateOpenSSL::verifyPeer()
-{
- // DTLSTODO: Windows-specific code for CA fetcher is not here yet.
- QVector<QSslError> errors;
-
- // Check the whole chain for blacklisting (including root, as we check for
- // subjectInfo and issuer)
- for (const QSslCertificate &cert : qAsConst(dtlsConfiguration.peerCertificateChain)) {
- if (QSslCertificatePrivate::isBlacklisted(cert))
- errors << QSslError(QSslError::CertificateBlacklisted, cert);
- }
-
- if (dtlsConfiguration.peerCertificate.isNull()) {
- errors << QSslError(QSslError::NoPeerCertificate);
- } else if (mode == QSslSocket::SslClientMode) {
- // Check the peer certificate itself. First try the subject's common name
- // (CN) as a wildcard, then try all alternate subject name DNS entries the
- // same way.
-
- // QSslSocket has a rather twisted logic: if verificationPeerName
- // is empty, we call QAbstractSocket::peerName(), which returns
- // either peerName (can be set by setPeerName) or host name
- // (can be set as a result of connectToHost).
- QString name = peerVerificationName;
- if (name.isEmpty()) {
- Q_ASSERT(dtls.udpSocket);
- name = dtls.udpSocket->peerName();
- }
-
- if (!QSslSocketPrivate::isMatchingHostname(dtlsConfiguration.peerCertificate, name))
- errors << QSslError(QSslError::HostNameMismatch, dtlsConfiguration.peerCertificate);
- }
-
- // Translate errors from the error list into QSslErrors
- errors.reserve(errors.size() + opensslErrors.size());
- for (const auto &error : qAsConst(opensslErrors)) {
- errors << _q_OpenSSL_to_QSslError(error.code,
- dtlsConfiguration.peerCertificateChain.value(error.depth));
- }
-
- tlsErrors = errors;
- return tlsErrors.isEmpty();
-}
-
-void QDtlsPrivateOpenSSL::storePeerCertificates()
-{
- Q_ASSERT(dtls.tlsConnection.data());
- // Store the peer certificate and chain. For clients, the peer certificate
- // chain includes the peer certificate; for servers, it doesn't. Both the
- // peer certificate and the chain may be empty if the peer didn't present
- // any certificate.
- X509 *x509 = q_SSL_get_peer_certificate(dtls.tlsConnection.data());
- dtlsConfiguration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
- q_X509_free(x509);
- if (dtlsConfiguration.peerCertificateChain.isEmpty()) {
- auto stack = q_SSL_get_peer_cert_chain(dtls.tlsConnection.data());
- dtlsConfiguration.peerCertificateChain = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(stack);
- if (!dtlsConfiguration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
- dtlsConfiguration.peerCertificateChain.prepend(dtlsConfiguration.peerCertificate);
- }
-}
-
-bool QDtlsPrivateOpenSSL::tlsErrorsWereIgnored() const
-{
- // check whether the errors we got are all in the list of expected errors
- // (applies only if the method QDtlsConnection::ignoreTlsErrors(const
- // QVector<QSslError> &errors) was called)
- for (const QSslError &error : tlsErrors) {
- if (!tlsErrorsToIgnore.contains(error))
- return false;
- }
-
- return !tlsErrorsToIgnore.empty();
-}
-
-void QDtlsPrivateOpenSSL::fetchNegotiatedParameters()
-{
- Q_ASSERT(dtls.tlsConnection.data());
-
- const SSL_CIPHER *cipher = q_SSL_get_current_cipher(dtls.tlsConnection.data());
- sessionCipher = cipher ? QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher)
- : QSslCipher();
-
- // Note: cipher's protocol version will be reported as either TLS 1.0 or
- // TLS 1.2, that's how it's set by OpenSSL (and that's what they are?).
-
- switch (q_SSL_version(dtls.tlsConnection.data())) {
- case DTLS1_VERSION:
- sessionProtocol = QSsl::DtlsV1_0;
- break;
- case DTLS1_2_VERSION:
- sessionProtocol = QSsl::DtlsV1_2;
- break;
- default:
- qCWarning(lcSsl, "unknown protocol version");
- sessionProtocol = QSsl::UnknownProtocol;
- }
-}
-
-void QDtlsPrivateOpenSSL::reportTimeout()
-{
- Q_Q(QDtls);
-
- emit q->handshakeTimeout();
-}
-
-void QDtlsPrivateOpenSSL::resetDtls()
-{
- dtls.reset();
- connectionEncrypted = false;
- tlsErrors.clear();
- tlsErrorsToIgnore.clear();
- dtlsConfiguration.peerCertificate.clear();
- dtlsConfiguration.peerCertificateChain.clear();
- connectionWasShutdown = false;
- handshakeState = QDtls::HandshakeNotStarted;
- sessionCipher = {};
- sessionProtocol = QSsl::UnknownProtocol;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qdtls_openssl_p.h b/src/network/ssl/qdtls_openssl_p.h
deleted file mode 100644
index 35e9f29a5d..0000000000
--- a/src/network/ssl/qdtls_openssl_p.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QDTLS_OPENSSL_P_H
-#define QDTLS_OPENSSL_P_H
-
-#include <private/qtnetworkglobal_p.h>
-
-#include <QtCore/qglobal.h>
-
-#include <openssl/ossl_typ.h>
-
-#include "qdtls_p.h"
-
-#include <private/qsslcontext_openssl_p.h>
-#include <private/qsslsocket_openssl_p.h>
-
-#include <QtNetwork/qsslpresharedkeyauthenticator.h>
-#include <QtNetwork/qhostaddress.h>
-
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qsharedpointer.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qvector.h>
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-QT_REQUIRE_CONFIG(openssl);
-QT_REQUIRE_CONFIG(dtls);
-
-QT_BEGIN_NAMESPACE
-
-class QDtlsPrivateOpenSSL;
-class QUdpSocket;
-
-namespace dtlsopenssl
-{
-
-class DtlsState
-{
-public:
- // Note, bioMethod _must_ outlive BIOs it was used to create. Thus
- // the order of declarations here matters.
- using BioMethod = QSharedPointer<BIO_METHOD>;
- BioMethod bioMethod;
-
- using TlsContext = QSharedPointer<QSslContext>;
- TlsContext tlsContext;
-
- using TlsConnection = QSharedPointer<SSL>;
- TlsConnection tlsConnection;
-
- QByteArray dgram;
-
- QHostAddress remoteAddress;
- quint16 remotePort = 0;
-
- QVector<QSslErrorEntry> x509Errors;
-
- long peeking = false;
- QUdpSocket *udpSocket = nullptr;
- bool writeSuppressed = false;
-
- bool init(QDtlsBasePrivate *dtlsBase, QUdpSocket *socket,
- const QHostAddress &remote, quint16 port,
- const QByteArray &receivedMessage);
-
- void reset();
-
- QDtlsPrivateOpenSSL *dtlsPrivate = nullptr;
- QByteArray secret;
-
-#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
- QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
-#else
- QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
-#endif
-
-private:
-
- bool initTls(QDtlsBasePrivate *dtlsBase);
- bool initCtxAndConnection(QDtlsBasePrivate *dtlsBase);
- bool initBIO(QDtlsBasePrivate *dtlsBase);
- void setLinkMtu(QDtlsBasePrivate *dtlsBase);
-};
-
-} // namespace dtlsopenssl
-
-class QDtlsClientVerifierOpenSSL : public QDtlsClientVerifierPrivate
-{
-public:
-
- QDtlsClientVerifierOpenSSL();
-
- bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
- const QHostAddress &address, quint16 port) override;
-
-private:
- dtlsopenssl::DtlsState dtls;
-};
-
-class QDtlsPrivateOpenSSL : public QDtlsPrivate
-{
-public:
- QDtlsPrivateOpenSSL();
-
- bool startHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
- bool continueHandshake(QUdpSocket *socket, const QByteArray &datagram) override;
- bool resumeHandshake(QUdpSocket *socket) override;
- void abortHandshake(QUdpSocket *socket) override;
- bool handleTimeout(QUdpSocket *socket) override;
- void sendShutdownAlert(QUdpSocket *socket) override;
-
- qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &datagram) override;
- QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &tlsdgram) override;
-
- unsigned pskClientCallback(const char *hint, char *identity, unsigned max_identity_len,
- unsigned char *psk, unsigned max_psk_len);
- unsigned pskServerCallback(const char *identity, unsigned char *psk,
- unsigned max_psk_len);
-
-private:
-
- bool verifyPeer();
- void storePeerCertificates();
- bool tlsErrorsWereIgnored() const;
- void fetchNegotiatedParameters();
- void reportTimeout();
- void resetDtls();
-
- QVector<QSslErrorEntry> opensslErrors;
- dtlsopenssl::DtlsState dtls;
-
- // We have to externally handle timeouts since we have non-blocking
- // sockets and OpenSSL(DTLS) with non-blocking UDP sockets does not
- // know if a timeout has occurred.
- struct TimeoutHandler : QObject
- {
- TimeoutHandler() = default;
-
- void start(int hintMs = 0);
- void doubleTimeout();
- void resetTimeout() {timeoutMs = 1000;}
- void stop();
- void timerEvent(QTimerEvent *event);
-
- int timerId = -1;
- int timeoutMs = 1000;
-
- QDtlsPrivateOpenSSL *dtlsConnection = nullptr;
- };
-
- // We will initialize it 'lazily', just in case somebody wants to move
- // QDtls to another thread.
- QScopedPointer<TimeoutHandler> timeoutHandler;
- bool connectionWasShutdown = false;
- QSslPreSharedKeyAuthenticator pskAuthenticator;
- QByteArray identityHint;
-
- Q_DECLARE_PUBLIC(QDtls)
-};
-
-
-
-QT_END_NAMESPACE
-
-#endif // QDTLS_OPENSSL_P_H
diff --git a/src/network/ssl/qdtls_p.h b/src/network/ssl/qdtls_p.h
index bdc001502b..5d519e2344 100644
--- a/src/network/ssl/qdtls_p.h
+++ b/src/network/ssl/qdtls_p.h
@@ -1,62 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDTLS_P_H
#define QDTLS_P_H
#include <private/qtnetworkglobal_p.h>
-#include "qdtls.h"
-
-#include <private/qsslconfiguration_p.h>
-#include <private/qobject_p.h>
-
-#include <QtNetwork/qabstractsocket.h>
-#include <QtNetwork/qhostaddress.h>
-#include <QtNetwork/qsslsocket.h>
-#include <QtNetwork/qsslcipher.h>
-#include <QtNetwork/qssl.h>
-
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qstring.h>
+#include "qtlsbackend_p.h"
+#include <QtCore/private/qobject_p.h>
//
// W A R N I N G
// -------------
@@ -74,80 +26,20 @@ QT_BEGIN_NAMESPACE
class QHostAddress;
-class QDtlsBasePrivate : public QObjectPrivate
-{
-public:
-
- void setDtlsError(QDtlsError code, const QString &description)
- {
- errorCode = code;
- errorDescription = description;
- }
-
- void clearDtlsError()
- {
- errorCode = QDtlsError::NoError;
- errorDescription.clear();
- }
-
- void setConfiguration(const QSslConfiguration &configuration);
- QSslConfiguration configuration() const;
-
- bool setCookieGeneratorParameters(QCryptographicHash::Algorithm alg,
- const QByteArray &secret);
-
- static bool isDtlsProtocol(QSsl::SslProtocol protocol);
-
- QHostAddress remoteAddress;
- quint16 remotePort = 0;
- quint16 mtuHint = 0;
-
- QDtlsError errorCode = QDtlsError::NoError;
- QString errorDescription;
- QSslConfigurationPrivate dtlsConfiguration;
- QSslSocket::SslMode mode = QSslSocket::SslClientMode;
- QSslCipher sessionCipher;
- QSsl::SslProtocol sessionProtocol = QSsl::UnknownProtocol;
- QString peerVerificationName;
- QByteArray secret;
-
-#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
- QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
-#else
- QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
-#endif
-};
-
-class QDtlsClientVerifierPrivate : public QDtlsBasePrivate
+class QDtlsClientVerifierPrivate : public QObjectPrivate
{
public:
-
- QByteArray verifiedClientHello;
-
- virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
- const QHostAddress &address, quint16 port) = 0;
+ QDtlsClientVerifierPrivate();
+ ~QDtlsClientVerifierPrivate();
+ std::unique_ptr<QTlsPrivate::DtlsCookieVerifier> backend;
};
-class QDtlsPrivate : public QDtlsBasePrivate
+class QDtlsPrivate : public QObjectPrivate
{
public:
-
- virtual bool startHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
- virtual bool handleTimeout(QUdpSocket *socket) = 0;
- virtual bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
- virtual bool resumeHandshake(QUdpSocket *socket) = 0;
- virtual void abortHandshake(QUdpSocket *socket) = 0;
- virtual void sendShutdownAlert(QUdpSocket *socket) = 0;
-
- virtual qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) = 0;
- virtual QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) = 0;
-
- QDtls::HandshakeState handshakeState = QDtls::HandshakeNotStarted;
-
- QVector<QSslError> tlsErrors;
- QVector<QSslError> tlsErrorsToIgnore;
-
- bool connectionEncrypted = false;
+ QDtlsPrivate();
+ ~QDtlsPrivate();
+ std::unique_ptr<QTlsPrivate::DtlsCryptograph> backend;
};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qocsp_p.h b/src/network/ssl/qocsp_p.h
index 71f59da0b4..596cb1357f 100644
--- a/src/network/ssl/qocsp_p.h
+++ b/src/network/ssl/qocsp_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOCSP_P_H
#define QOCSP_P_H
diff --git a/src/network/ssl/qocspresponse.cpp b/src/network/ssl/qocspresponse.cpp
index bf27bb768b..74e2c814fd 100644
--- a/src/network/ssl/qocspresponse.cpp
+++ b/src/network/ssl/qocspresponse.cpp
@@ -1,41 +1,6 @@
-/****************************************************************************
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qocspresponse_p.h"
#include "qocspresponse.h"
@@ -44,6 +9,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QOcspResponse)
+
/*!
\class QOcspResponse
\brief This class represents Online Certificate Status Protocol response.
@@ -53,7 +20,7 @@ QT_BEGIN_NAMESPACE
\ingroup ssl
\inmodule QtNetwork
- The QOcspResponse class represents the revocation status of a server's certficate,
+ The QOcspResponse class represents the revocation status of a server's certificate,
received by the client-side socket during the TLS handshake. QSslSocket must be
configured with OCSP stapling enabled.
@@ -95,7 +62,7 @@ QT_BEGIN_NAMESPACE
\inmodule QtNetwork
- This enumeration describes revocation reasons, defined in \l{https://tools.ietf.org/html/rfc5280#section-5.3.1}{RFC 5280, section 5.3.1}
+ This enumeration describes revocation reasons, defined in \l{RFC 5280, section 5.3.1}
\value None
\value Unspecified
@@ -145,14 +112,14 @@ QOcspResponse::~QOcspResponse() = default;
/*!
\since 5.13
- Copy-assigns and returns a reference to this response.
+ Copy-assigns \a other and returns a reference to this response.
*/
QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default;
/*!
\since 5.13
- Move-assigns to this QOcspResponse instance.
+ Move-assigns \a other to this QOcspResponse instance.
*/
QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) noexcept = default;
@@ -206,46 +173,46 @@ QSslCertificate QOcspResponse::subject() const
}
/*!
- \fn bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
+ \fn bool QOcspResponse::operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
Returns \c true if \a lhs and \a rhs are the responses for the same
certificate, signed by the same responder, have the same
revocation reason and the same certificate status.
\since 5.13
- \relates QOcspResponse
- */
-Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
-{
- return lhs.d == rhs.d || *lhs.d == *rhs.d;
-}
+*/
/*!
- \fn bool operator != (const QOcspResponse &lhs, const QOcspResponse &rhs)
+ \fn bool QOcspResponse::operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs)
- Returns \c true if \a lhs and \a rhs are responses for different certificates,
- or signed by different responders, or have different revocation reasons, or different
- certificate statuses.
+ Returns \c true if \a lhs and \a rhs are responses for different certificates,
+ or signed by different responders, or have different revocation reasons, or different
+ certificate statuses.
- \since 5.13
- \relates QOcspResponse
+ \since 5.13
*/
/*!
- \fn uint qHash(const QOcspResponse &response, uint seed)
+ \internal
+*/
+bool QOcspResponse::isEqual(const QOcspResponse &other) const
+{
+ return d == other.d || *d == *other.d;
+}
+/*!
Returns the hash value for the \a response, using \a seed to seed the calculation.
\since 5.13
\relates QHash
*/
-uint qHash(const QOcspResponse &response, uint seed) noexcept
+size_t qHash(const QOcspResponse &response, size_t seed) noexcept
{
const QOcspResponsePrivate *d = response.d.data();
Q_ASSERT(d);
QtPrivate::QHashCombine hasher;
- uint hash = hasher(seed, int(d->certificateStatus));
+ size_t hash = hasher(seed, int(d->certificateStatus));
hash = hasher(hash, int(d->revocationReason));
if (!d->signerCert.isNull())
hash = hasher(hash, d->signerCert);
diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h
index cf6be5a369..68251a1547 100644
--- a/src/network/ssl/qocspresponse.h
+++ b/src/network/ssl/qocspresponse.h
@@ -1,41 +1,6 @@
-/****************************************************************************
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOCSPRESPONSE_H
#define QOCSPRESPONSE_H
@@ -46,7 +11,7 @@
#include <QtCore/qmetatype.h>
#include <QtCore/qobject.h>
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
QT_REQUIRE_CONFIG(ssl);
#endif
@@ -72,8 +37,12 @@ enum class QOcspRevocationReason
RemoveFromCRL
};
+namespace QTlsPrivate {
+class TlsCryptographOpenSSL;
+}
+
class QOcspResponse;
-Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed = 0) noexcept;
class QOcspResponsePrivate;
class Q_NETWORK_EXPORT QOcspResponse
@@ -97,20 +66,23 @@ public:
void swap(QOcspResponse &other) noexcept { d.swap(other.d); }
private:
+ bool isEqual(const QOcspResponse &other) const;
+
+ friend class QTlsPrivate::TlsCryptographOpenSSL;
+ friend bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs)
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs)
+ { return !lhs.isEqual(rhs); }
- friend class QSslSocketBackendPrivate;
- friend Q_NETWORK_EXPORT bool operator==(const QOcspResponse &lhs, const QOcspResponse &rhs);
- friend Q_NETWORK_EXPORT uint qHash(const QOcspResponse &response, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QOcspResponse &response, size_t seed) noexcept;
QSharedDataPointer<QOcspResponsePrivate> d;
};
-inline bool operator!=(const QOcspResponse &lhs, const QOcspResponse &rhs) { return !(lhs == rhs); }
-
Q_DECLARE_SHARED(QOcspResponse)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QOcspResponse)
+QT_DECL_METATYPE_EXTERN(QOcspResponse, Q_NETWORK_EXPORT)
#endif // QOCSPRESPONSE_H
diff --git a/src/network/ssl/qocspresponse_p.h b/src/network/ssl/qocspresponse_p.h
index e421b76899..5f08e306cd 100644
--- a/src/network/ssl/qocspresponse_p.h
+++ b/src/network/ssl/qocspresponse_p.h
@@ -1,41 +1,6 @@
-/****************************************************************************
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOCSPRESPONSE_P_H
#define QOCSPRESPONSE_P_H
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp
index 706fa1de05..94de14abd4 100644
--- a/src/network/ssl/qpassworddigestor.cpp
+++ b/src/network/ssl/qpassworddigestor.cpp
@@ -1,50 +1,25 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qpassworddigestor.h"
#include <QtCore/QDebug>
#include <QtCore/QMessageAuthenticationCode>
#include <QtCore/QtEndian>
+#include <QtCore/QList>
+
+#include "qtcore-config_p.h"
#include <limits>
+#if QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked)
+#define USING_OPENSSL30
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/provider.h>
+#endif
+
QT_BEGIN_NAMESPACE
namespace QPasswordDigestor {
@@ -60,7 +35,7 @@ namespace QPasswordDigestor {
\since 5.12
Returns a hash computed using the PBKDF1-algorithm as defined in
- \l {https://tools.ietf.org/html/rfc8018#section-5.1} {RFC 8018}.
+ \l {RFC 8018, section 5.1}.
The function takes the \a data and \a salt, and then hashes it repeatedly
for \a iterations iterations using the specified hash \a algorithm. If the
@@ -122,11 +97,90 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algori
return key.left(dkLen);
}
+#ifdef USING_OPENSSL30
+// Copied from QCryptographicHashPrivate
+static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
+{
+ switch (method) {
+#define CASE(Enum, Name) \
+ case QCryptographicHash:: Enum : \
+ return Name \
+ /*end*/
+ CASE(Sha1, "SHA1");
+ CASE(Md4, "MD4");
+ CASE(Md5, "MD5");
+ CASE(Sha224, "SHA224");
+ CASE(Sha256, "SHA256");
+ CASE(Sha384, "SHA384");
+ CASE(Sha512, "SHA512");
+ CASE(RealSha3_224, "SHA3-224");
+ CASE(RealSha3_256, "SHA3-256");
+ CASE(RealSha3_384, "SHA3-384");
+ CASE(RealSha3_512, "SHA3-512");
+ CASE(Keccak_224, "SHA3-224");
+ CASE(Keccak_256, "SHA3-256");
+ CASE(Keccak_384, "SHA3-384");
+ CASE(Keccak_512, "SHA3-512");
+ CASE(Blake2b_512, "BLAKE2B512");
+ CASE(Blake2s_256, "BLAKE2S256");
+#undef CASE
+ default: return nullptr;
+ }
+}
+
+static QByteArray opensslDeriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &data, const QByteArray &salt,
+ uint64_t iterations, quint64 dkLen)
+{
+ EVP_KDF *kdf = EVP_KDF_fetch(nullptr, "PBKDF2", nullptr);
+
+ if (!kdf)
+ return QByteArray();
+
+ auto cleanUpKdf = qScopeGuard([kdf] {
+ EVP_KDF_free(kdf);
+ });
+
+ EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+
+ if (!ctx)
+ return QByteArray();
+
+ auto cleanUpCtx = qScopeGuard([ctx] {
+ EVP_KDF_CTX_free(ctx);
+ });
+
+ // Do not enable SP800-132 compliance check, otherwise we will require:
+ // - the iteration count is at least 1000
+ // - the salt length is at least 128 bits
+ // - the derived key length is at least 112 bits
+ // This would be a different behavior from the original implementation.
+ int checkDisabled = 1;
+ QList<OSSL_PARAM> params;
+ params.append(OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(methodToName(algorithm)), 0));
+ params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, const_cast<char*>(salt.data()), salt.size()));
+ params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast<char*>(data.data()), data.size()));
+ params.append(OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_ITER, &iterations));
+ params.append(OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &checkDisabled));
+ params.append(OSSL_PARAM_construct_end());
+
+ if (EVP_KDF_CTX_set_params(ctx, params.data()) <= 0)
+ return QByteArray();
+
+ QByteArray derived(dkLen, '\0');
+
+ if (!EVP_KDF_derive(ctx, reinterpret_cast<unsigned char*>(derived.data()), derived.size(), nullptr))
+ return QByteArray();
+
+ return derived;
+}
+#endif
+
/*!
\since 5.12
Derive a key using the PBKDF2-algorithm as defined in
- \l {https://tools.ietf.org/html/rfc8018#section-5.2} {RFC 8018}.
+ \l {RFC 8018, section 5.2}.
This function takes the \a data and \a salt, and then applies HMAC-X, where
the X is \a algorithm, repeatedly. It internally concatenates intermediate
@@ -143,8 +197,6 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
const QByteArray &data, const QByteArray &salt,
int iterations, quint64 dkLen)
{
- // https://tools.ietf.org/html/rfc8018#section-5.2
-
// The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
int hashLen = QCryptographicHash::hashLength(algorithm);
const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen;
@@ -158,11 +210,17 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
if (iterations < 1 || dkLen < 1)
return QByteArray();
+#ifdef USING_OPENSSL30
+ if (methodToName(algorithm))
+ return opensslDeriveKeyPbkdf2(algorithm, data, salt, iterations, dkLen);
+#endif
+
+ // https://tools.ietf.org/html/rfc8018#section-5.2
QByteArray key;
quint32 currentIteration = 1;
QMessageAuthenticationCode hmac(algorithm, data);
QByteArray index(4, Qt::Uninitialized);
- while (quint64(key.length()) < dkLen) {
+ while (quint64(key.size()) < dkLen) {
hmac.addData(salt);
qToBigEndian(currentIteration, index.data());
diff --git a/src/network/ssl/qpassworddigestor.h b/src/network/ssl/qpassworddigestor.h
index 0f88643298..279450178b 100644
--- a/src/network/ssl/qpassworddigestor.h
+++ b/src/network/ssl/qpassworddigestor.h
@@ -1,45 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPASSWORDDIGESTOR_H
#define QPASSWORDDIGESTOR_H
+#if 0
+#pragma qt_class(QPasswordDigestor)
+#endif
+
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QByteArray>
#include <QtCore/QCryptographicHash>
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index c9fa7f85d9..dfd3745d3e 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsslkey.h"
@@ -102,33 +66,20 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value IpAddressEntry An IP address entry; the entry contains an IP address
entry that the certificate is valid for, introduced in Qt 5.13.
- \note In Qt 4, this enum was called \c {AlternateNameEntryType}. That name
- is deprecated in Qt 5.
-
\sa QSslCertificate::subjectAlternativeNames()
*/
/*!
- \typedef QSsl::AlternateNameEntryType
- \obsolete
-
- Use QSsl::AlternativeNameEntryType instead.
-*/
-
-/*!
\enum QSsl::SslProtocol
Describes the protocol of the cipher.
- \value SslV3 SSLv3; not supported by QSslSocket.
- \value SslV2 SSLv2; not supported by QSslSocket.
\value TlsV1_0 TLSv1.0
- \value TlsV1_0OrLater TLSv1.0 and later versions. This option is not available when using the WinRT backend due to platform limitations.
- \value TlsV1 Obsolete, means the same as TlsV1_0
- \value TlsV1_1 TLSv1.1. When using the WinRT backend this option will also enable TLSv1.0.
- \value TlsV1_1OrLater TLSv1.1 and later versions. This option is not available when using the WinRT backend due to platform limitations.
- \value TlsV1_2 TLSv1.2. When using the WinRT backend this option will also enable TLSv1.0 and TLSv1.1.
- \value TlsV1_2OrLater TLSv1.2 and later versions. This option is not available when using the WinRT backend due to platform limitations.
+ \value TlsV1_0OrLater TLSv1.0 and later versions.
+ \value TlsV1_1 TLSv1.1.
+ \value TlsV1_1OrLater TLSv1.1 and later versions.
+ \value TlsV1_2 TLSv1.2.
+ \value TlsV1_2OrLater TLSv1.2 and later versions.
\value DtlsV1_0 DTLSv1.0
\value DtlsV1_0OrLater DTLSv1.0 and later versions.
\value DtlsV1_2 DTLSv1.2
@@ -137,7 +88,6 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value TlsV1_3OrLater TLSv1.3 and later versions. (Since Qt 5.12)
\value UnknownProtocol The cipher's protocol cannot be determined.
\value AnyProtocol Any supported protocol. This value is used by QSslSocket only.
- \value TlsV1SslV3 Same as TlsV1_0.
\value SecureProtocols The default option, using protocols known to be secure.
*/
@@ -189,5 +139,122 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
backend in use.
*/
+/*!
+ \enum QSsl::AlertLevel
+ \brief Describes the level of an alert message
+ \relates QSslSocket
+ \since 6.0
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ This enum describes the level of an alert message that was sent
+ or received.
+
+ \value Warning Non-fatal alert message
+ \value Fatal Fatal alert message, the underlying backend will
+ handle such an alert properly and close the connection.
+ \value Unknown An alert of unknown level of severity.
+*/
+
+/*!
+ \enum QSsl::AlertType
+ \brief Enumerates possible codes that an alert message can have
+ \relates QSslSocket
+ \since 6.0
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ See \l{RFC 8446, section 6}
+ for the possible values and their meaning.
+
+ \value CloseNotify,
+ \value UnexpectedMessage
+ \value BadRecordMac
+ \value RecordOverflow
+ \value DecompressionFailure
+ \value HandshakeFailure
+ \value NoCertificate
+ \value BadCertificate
+ \value UnsupportedCertificate
+ \value CertificateRevoked
+ \value CertificateExpired
+ \value CertificateUnknown
+ \value IllegalParameter
+ \value UnknownCa
+ \value AccessDenied
+ \value DecodeError
+ \value DecryptError
+ \value ExportRestriction
+ \value ProtocolVersion
+ \value InsufficientSecurity
+ \value InternalError
+ \value InappropriateFallback
+ \value UserCancelled
+ \value NoRenegotiation
+ \value MissingExtension
+ \value UnsupportedExtension
+ \value CertificateUnobtainable
+ \value UnrecognizedName
+ \value BadCertificateStatusResponse
+ \value BadCertificateHashValue
+ \value UnknownPskIdentity
+ \value CertificateRequired
+ \value NoApplicationProtocol
+ \value UnknownAlertMessage
+*/
+
+/*!
+ \enum QSsl::ImplementedClass
+ \brief Enumerates classes that a TLS backend implements
+ \relates QSslSocket
+ \since 6.1
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ In QtNetwork, some classes have backend-specific implementation and thus
+ can be left unimplemented. Enumerators in this enum indicate, which class
+ has a working implementation in the backend.
+
+ \value Key Class QSslKey.
+ \value Certificate Class QSslCertificate.
+ \value Socket Class QSslSocket.
+ \value DiffieHellman Class QSslDiffieHellmanParameters.
+ \value EllipticCurve Class QSslEllipticCurve.
+ \value Dtls Class QDtls.
+ \value DtlsCookie Class QDtlsClientVerifier.
+*/
+
+/*!
+ \enum QSsl::SupportedFeature
+ \brief Enumerates possible features that a TLS backend supports
+ \relates QSslSocket
+ \since 6.1
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ In QtNetwork TLS-related classes have public API, that may be left unimplemented
+ by some backend, for example, our SecureTransport backend does not support
+ server-side ALPN. Enumerators from SupportedFeature enum indicate that a particular
+ feature is supported.
+
+ \value CertificateVerification Indicates that QSslCertificate::verify() is
+ implemented by the backend.
+ \value ClientSideAlpn Client-side ALPN (Application Layer Protocol Negotiation).
+ \value ServerSideAlpn Server-side ALPN.
+ \value Ocsp OCSP stapling (Online Certificate Status Protocol).
+ \value Psk Pre-shared keys.
+ \value SessionTicket Session tickets.
+ \value Alerts Information about alert messages sent and received.
+*/
QT_END_NAMESPACE
+
+#include "moc_qssl.cpp"
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index b28c2a87b9..e52b8c6361 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -1,62 +1,35 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSL_H
#define QSSL_H
+#if 0
+#pragma qt_class(QSsl)
+#endif
+
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qobjectdefs.h>
#include <QtCore/QFlags>
QT_BEGIN_NAMESPACE
namespace QSsl {
+ Q_NAMESPACE_EXPORT(Q_NETWORK_EXPORT)
+
enum KeyType {
PrivateKey,
PublicKey
};
+ Q_ENUM_NS(KeyType)
enum EncodingFormat {
Pem,
Der
};
+ Q_ENUM_NS(EncodingFormat)
enum KeyAlgorithm {
Opaque,
@@ -65,40 +38,28 @@ namespace QSsl {
Ec,
Dh,
};
+ Q_ENUM_NS(KeyAlgorithm)
enum AlternativeNameEntryType {
EmailEntry,
DnsEntry,
IpAddressEntry
};
-
-#if QT_DEPRECATED_SINCE(5,0)
- typedef AlternativeNameEntryType AlternateNameEntryType;
-#endif
+ Q_ENUM_NS(AlternativeNameEntryType)
enum SslProtocol {
-#if QT_DEPRECATED_SINCE(5, 15)
- SslV3,
- SslV2,
-#endif
- TlsV1_0 = 2,
-#if QT_DEPRECATED_SINCE(5,0)
- TlsV1 = TlsV1_0,
-#endif
- TlsV1_1,
+ TlsV1_0 QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."),
+ TlsV1_1 QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."),
TlsV1_2,
AnyProtocol,
-#if QT_DEPRECATED_SINCE(5, 15)
- TlsV1SslV3,
-#endif
- SecureProtocols = AnyProtocol + 2,
+ SecureProtocols,
- TlsV1_0OrLater,
- TlsV1_1OrLater,
+ TlsV1_0OrLater QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."),
+ TlsV1_1OrLater QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."),
TlsV1_2OrLater,
- DtlsV1_0,
- DtlsV1_0OrLater,
+ DtlsV1_0 QT_DEPRECATED_VERSION_X_6_3("Use DtlsV1_2OrLater instead."),
+ DtlsV1_0OrLater QT_DEPRECATED_VERSION_X_6_3("Use DtlsV1_2OrLater instead."),
DtlsV1_2,
DtlsV1_2OrLater,
@@ -107,6 +68,7 @@ namespace QSsl {
UnknownProtocol = -1
};
+ Q_ENUM_NS(SslProtocol)
enum SslOption {
SslOptionDisableEmptyFragments = 0x01,
@@ -118,7 +80,77 @@ namespace QSsl {
SslOptionDisableSessionPersistence = 0x40,
SslOptionDisableServerCipherPreference = 0x80
};
+ Q_ENUM_NS(SslOption)
Q_DECLARE_FLAGS(SslOptions, SslOption)
+
+ enum class AlertLevel {
+ Warning,
+ Fatal,
+ Unknown
+ };
+ Q_ENUM_NS(AlertLevel)
+
+ enum class AlertType {
+ CloseNotify,
+ UnexpectedMessage = 10,
+ BadRecordMac = 20,
+ RecordOverflow = 22,
+ DecompressionFailure = 30, // reserved
+ HandshakeFailure = 40,
+ NoCertificate = 41, // reserved
+ BadCertificate = 42,
+ UnsupportedCertificate = 43,
+ CertificateRevoked = 44,
+ CertificateExpired = 45,
+ CertificateUnknown = 46,
+ IllegalParameter = 47,
+ UnknownCa = 48,
+ AccessDenied = 49,
+ DecodeError = 50,
+ DecryptError = 51,
+ ExportRestriction = 60, // reserved
+ ProtocolVersion = 70,
+ InsufficientSecurity = 71,
+ InternalError = 80,
+ InappropriateFallback = 86,
+ UserCancelled = 90,
+ NoRenegotiation = 100,
+ MissingExtension = 109,
+ UnsupportedExtension = 110,
+ CertificateUnobtainable = 111, // reserved
+ UnrecognizedName = 112,
+ BadCertificateStatusResponse = 113,
+ BadCertificateHashValue = 114, // reserved
+ UnknownPskIdentity = 115,
+ CertificateRequired = 116,
+ NoApplicationProtocol = 120,
+ UnknownAlertMessage = 255
+ };
+ Q_ENUM_NS(AlertType)
+
+ enum class ImplementedClass
+ {
+ Key,
+ Certificate,
+ Socket,
+ DiffieHellman,
+ EllipticCurve,
+ Dtls,
+ DtlsCookie
+ };
+ Q_ENUM_NS(ImplementedClass)
+
+ enum class SupportedFeature
+ {
+ CertificateVerification,
+ ClientSideAlpn,
+ ServerSideAlpn,
+ Ocsp,
+ Psk,
+ SessionTicket,
+ Alerts
+ };
+ Q_ENUM_NS(SupportedFeature)
}
Q_DECLARE_OPERATORS_FOR_FLAGS(QSsl::SslOptions)
diff --git a/src/network/ssl/qssl_p.h b/src/network/ssl/qssl_p.h
index 83ccdc7fc3..ccbcf87029 100644
--- a/src/network/ssl/qssl_p.h
+++ b/src/network/ssl/qssl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSL_P_H
@@ -60,6 +24,19 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcSsl)
+namespace QTlsPrivate {
+
+enum class Cipher {
+ DesCbc,
+ DesEde3Cbc,
+ Rc2Cbc,
+ Aes128Cbc,
+ Aes192Cbc,
+ Aes256Cbc
+};
+
+} // namespace QTlsPrivate
+
QT_END_NAMESPACE
#endif // QSSL_P_H
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 4820953468..9878c603b6 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -111,33 +75,65 @@
\value EmailAddress The email address associated with the certificate
*/
+/*!
+ \enum QSslCertificate::PatternSyntax
+ \since 5.15
+
+ The syntax used to interpret the meaning of the pattern.
+
+ \value RegularExpression A rich Perl-like pattern matching syntax.
+
+ \value Wildcard This provides a simple pattern matching syntax
+ similar to that used by shells (command interpreters) for "file
+ globbing". See \l {QRegularExpression::fromWildcard()}.
+
+ \value FixedString The pattern is a fixed string. This is
+ equivalent to using the RegularExpression pattern on a string in
+ which all metacharacters are escaped using escape(). This is the
+ default.
+*/
+
#include <QtNetwork/qtnetworkglobal.h>
-#ifndef QT_NO_OPENSSL
-#include "qsslsocket_openssl_symbols_p.h"
-#endif
-#ifdef Q_OS_WINRT
-#include "qsslsocket_winrt_p.h"
-#endif
-#ifdef QT_SECURETRANSPORT
-#include "qsslsocket_mac_p.h"
-#endif
-#if QT_CONFIG(schannel)
-#include "qsslsocket_schannel_p.h"
+
+#if QT_CONFIG(regularexpression)
+#include "qregularexpression.h"
#endif
-#include "qssl_p.h"
-#include "qsslcertificate.h"
+#include "qsslcertificateextension_p.h"
#include "qsslcertificate_p.h"
+#include "qsslcertificate.h"
+#include "qssl_p.h"
+
#ifndef QT_NO_SSL
+#include "qsslsocket_p.h"
#include "qsslkey_p.h"
#endif
#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
+#include <QtCore/qdirlisting.h>
#include <QtCore/qfile.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN(QSslCertificate)
+
+QSslCertificatePrivate::QSslCertificatePrivate()
+{
+#ifndef QT_NO_SSL
+ QSslSocketPrivate::ensureInitialized();
+#endif
+
+ const QTlsBackend *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (tlsBackend)
+ backend.reset(tlsBackend->createCertificate());
+ else
+ qCWarning(lcSsl, "No TLS backend is available");
+}
+
+QSslCertificatePrivate::~QSslCertificatePrivate() = default;
+
/*!
Constructs a QSslCertificate by reading \a format encoded data
from \a device and using the first certificate found. You can
@@ -147,13 +143,25 @@ QT_BEGIN_NAMESPACE
QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate)
{
-#ifndef QT_NO_OPENSSL
- QSslSocketPrivate::ensureInitialized();
- if (device && QSslSocket::supportsSsl())
-#else
- if (device)
-#endif
- d->init(device->readAll(), format);
+ if (device) {
+ const auto data = device->readAll();
+ if (data.isEmpty())
+ return;
+
+ const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (!tlsBackend)
+ return;
+
+ auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
+ if (!X509Reader) {
+ qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER");
+ return;
+ }
+
+ QList<QSslCertificate> certs = X509Reader(data, 1);
+ if (!certs.isEmpty())
+ d = certs.first().d;
+ }
}
/*!
@@ -165,11 +173,22 @@ QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
: d(new QSslCertificatePrivate)
{
-#ifndef QT_NO_OPENSSL
- QSslSocketPrivate::ensureInitialized();
- if (QSslSocket::supportsSsl())
-#endif
- d->init(data, format);
+ if (data.isEmpty())
+ return;
+
+ const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (!tlsBackend)
+ return;
+
+ auto *X509Reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
+ if (!X509Reader) {
+ qCWarning(lcSsl, "Current TLS plugin does not support reading from PEM/DER");
+ return;
+ }
+
+ const QList<QSslCertificate> certs = X509Reader(data, 1);
+ if (!certs.isEmpty())
+ d = certs.first().d;
}
/*!
@@ -211,6 +230,20 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
returns \c false.
*/
+bool QSslCertificate::operator==(const QSslCertificate &other) const
+{
+ if (d == other.d)
+ return true;
+
+ if (isNull() && other.isNull())
+ return true;
+
+ if (d->backend.get() && other.d->backend.get())
+ return d->backend->isEqual(*other.d->backend.get());
+
+ return false;
+}
+
/*!
\fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
@@ -228,25 +261,13 @@ QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
\sa clear()
*/
+bool QSslCertificate::isNull() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->isNull();
-#if QT_DEPRECATED_SINCE(5,0)
-/*!
- \fn bool QSslCertificate::isValid() const
- \obsolete
-
- To verify a certificate, use verify().
- To check if a certificate is blacklisted, use isBlacklisted().
- To check if a certificate has expired or is not yet valid, compare
- expiryDate() and effectiveDate() with QDateTime::currentDateTime()
-
- This function checks that the current
- date-time is within the date-time range during which the
- certificate is considered valid, and checks that the
- certificate is not in a blacklist of fraudulent certificates.
-
- \sa isNull(), verify(), isBlacklisted(), expiryDate(), effectiveDate()
-*/
-#endif
+ return true;
+}
/*!
Returns \c true if this certificate is blacklisted; otherwise
@@ -269,6 +290,13 @@ bool QSslCertificate::isBlacklisted() const
A certificate is considered self-signed its issuer and subject
are identical.
*/
+bool QSslCertificate::isSelfSigned() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->isSelfSigned();
+
+ return false;
+}
/*!
Clears the contents of this certificate, making it a null
@@ -287,12 +315,26 @@ void QSslCertificate::clear()
\fn QByteArray QSslCertificate::version() const
Returns the certificate's version string.
*/
+QByteArray QSslCertificate::version() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->version();
+
+ return {};
+}
/*!
\fn QByteArray QSslCertificate::serialNumber() const
Returns the certificate's serial number string in hexadecimal format.
*/
+QByteArray QSslCertificate::serialNumber() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->serialNumber();
+
+ return {};
+}
/*!
Returns a cryptographic digest of this certificate. By default,
@@ -314,6 +356,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
+QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->issuerInfo(info);
+
+ return {};
+}
/*!
\fn QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
@@ -324,6 +373,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
+QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->issuerInfo(attribute);
+
+ return {};
+}
/*!
\fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
@@ -334,6 +390,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa issuerInfo()
*/
+QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->subjectInfo(info);
+
+ return {};
+}
/*!
\fn QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
@@ -344,6 +407,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa issuerInfo()
*/
+QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->subjectInfo(attribute);
+
+ return {};
+}
/*!
\fn QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
@@ -357,6 +427,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
+QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->subjectInfoAttributes();
+
+ return {};
+}
/*!
\fn QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
@@ -370,15 +447,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
+QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->issuerInfoAttributes();
-#if QT_DEPRECATED_SINCE(5,0)
-/*!
- \fn QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
- \obsolete
-
- Use QSslCertificate::subjectAlternativeNames();
-*/
-#endif
+ return {};
+}
/*!
\fn QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
@@ -395,6 +470,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa subjectInfo()
*/
+QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->subjectAlternativeNames();
+
+ return {};
+}
/*!
\fn QDateTime QSslCertificate::effectiveDate() const
@@ -404,6 +486,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa expiryDate()
*/
+QDateTime QSslCertificate::effectiveDate() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->effectiveDate();
+
+ return {};
+}
/*!
\fn QDateTime QSslCertificate::expiryDate() const
@@ -413,6 +502,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\sa effectiveDate()
*/
+QDateTime QSslCertificate::expiryDate() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->expiryDate();
+
+ return {};
+}
/*!
\fn Qt::HANDLE QSslCertificate::handle() const
@@ -426,11 +522,29 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
non-portable, and its return value may vary from platform to
platform or change from minor release to minor release.
*/
+Qt::HANDLE QSslCertificate::handle() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->handle();
+ return {};
+}
+
+#ifndef QT_NO_SSL
/*!
\fn QSslKey QSslCertificate::publicKey() const
Returns the certificate subject's public key.
*/
+QSslKey QSslCertificate::publicKey() const
+{
+ QSslKey key;
+ if (const auto *backend = d->backend.get())
+ QTlsBackend::resetBackend(key, backend->publicKey());
+
+ return key;
+}
+#endif // QT_NO_SSL
+
/*!
\fn QList<QSslCertificateExtension> QSslCertificate::extensions() const
@@ -438,6 +552,10 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns a list containing the X509 extensions of this certificate.
\since 5.0
*/
+QList<QSslCertificateExtension> QSslCertificate::extensions() const
+{
+ return d->extensions();
+}
/*!
\fn QByteArray QSslCertificate::toPem() const
@@ -445,6 +563,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns this certificate converted to a PEM (Base64) encoded
representation.
*/
+QByteArray QSslCertificate::toPem() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->toPem();
+
+ return {};
+}
/*!
\fn QByteArray QSslCertificate::toDer() const
@@ -452,6 +577,13 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
Returns this certificate converted to a DER (binary) encoded
representation.
*/
+QByteArray QSslCertificate::toDer() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->toDer();
+
+ return {};
+}
/*!
\fn QString QSslCertificate::toText() const
@@ -461,21 +593,30 @@ QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) cons
\since 5.0
*/
+QString QSslCertificate::toText() const
+{
+ if (const auto *backend = d->backend.get())
+ return backend->toText();
+
+ return {};
+}
/*!
+ \since 5.15
+
Searches all files in the \a path for certificates encoded in the
specified \a format and returns them in a list. \a path must be a file
or a pattern matching one or more files, as specified by \a syntax.
Example:
- \snippet code/src_network_ssl_qsslcertificate.cpp 0
+ \snippet code/src_network_ssl_qsslcertificate.cpp 1
\sa fromData()
*/
QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
+ PatternSyntax syntax)
{
// $, (,), *, +, ., ?, [, ,], ^, {, | and }.
@@ -483,22 +624,30 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
QString sourcePath = QDir::fromNativeSeparators(path);
// Find the path without the filename
- QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(QLatin1Char('/')));
+ QStringView pathPrefix = QStringView(sourcePath).left(sourcePath.lastIndexOf(u'/'));
// Check if the path contains any special chars
int pos = -1;
- if (syntax == QRegExp::Wildcard)
- pos = pathPrefix.indexOf(QRegExp(QLatin1String("[*?[]")));
- else if (syntax != QRegExp::FixedString)
- pos = sourcePath.indexOf(QRegExp(QLatin1String("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
+
+#if QT_CONFIG(regularexpression)
+ if (syntax == PatternSyntax::Wildcard)
+ pos = pathPrefix.indexOf(QRegularExpression("[*?[]"_L1));
+ else if (syntax == PatternSyntax::RegularExpression)
+ pos = sourcePath.indexOf(QRegularExpression("[\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]"_L1));
+#else
+ if (syntax == PatternSyntax::Wildcard || syntax == PatternSyntax::RegExp)
+ qWarning("Regular expression support is disabled in this build. Only fixed string can be searched");
+ return QList<QSslCertificate>();
+#endif
+
if (pos != -1) {
// there was a special char in the path so cut of the part containing that char.
pathPrefix = pathPrefix.left(pos);
- const int lastIndexOfSlash = pathPrefix.lastIndexOf(QLatin1Char('/'));
+ const qsizetype lastIndexOfSlash = pathPrefix.lastIndexOf(u'/');
if (lastIndexOfSlash != -1)
pathPrefix = pathPrefix.left(lastIndexOfSlash);
else
- pathPrefix.clear();
+ pathPrefix = {};
} else {
// Check if the path is a file.
if (QFileInfo(sourcePath).isFile()) {
@@ -515,18 +664,36 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
// Special case - if the prefix ends up being nothing, use "." instead.
int startIndex = 0;
if (pathPrefix.isEmpty()) {
- pathPrefix = QLatin1String(".");
+ pathPrefix = u".";
startIndex = 2;
}
+ const QString pathPrefixString = pathPrefix.toString();
+
// The path can be a file or directory.
QList<QSslCertificate> certs;
- QRegExp pattern(sourcePath, Qt::CaseSensitive, syntax);
- QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
- while (it.hasNext()) {
- QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
- if (!pattern.exactMatch(filePath))
+
+#if QT_CONFIG(regularexpression)
+ if (syntax == PatternSyntax::Wildcard)
+ sourcePath = QRegularExpression::wildcardToRegularExpression(sourcePath, QRegularExpression::UnanchoredWildcardConversion);
+
+ QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath));
+#endif
+
+ using F = QDirListing::IteratorFlag;
+ constexpr auto iterFlags = F::FollowSymlinks | F::Recursive;
+ for (const auto &dirEntry : QDirListing(pathPrefixString, QDir::Files, iterFlags)) {
+ QString filePath = dirEntry.filePath();
+ if (startIndex > 0)
+ filePath.remove(0, startIndex);
+
+#if QT_CONFIG(regularexpression)
+ if (!pattern.match(filePath).hasMatch())
continue;
+#else
+ if (sourcePath != filePath)
+ continue;
+#endif
QFile file(filePath);
QIODevice::OpenMode openMode = QIODevice::ReadOnly;
@@ -563,13 +730,22 @@ QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::Enco
*/
QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
{
- return (format == QSsl::Pem)
- ? QSslCertificatePrivate::certificatesFromPem(data)
- : QSslCertificatePrivate::certificatesFromDer(data);
+ const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend is available");
+ return {};
+ }
+
+ auto reader = format == QSsl::Pem ? tlsBackend->X509PemReader() : tlsBackend->X509DerReader();
+ if (!reader) {
+ qCWarning(lcSsl, "The available TLS backend does not support reading PEM/DER");
+ return {};
+ }
+
+ return reader(data, -1);
}
#ifndef QT_NO_SSL
-
/*!
Verifies a certificate chain. The chain to be verified is passed in the
\a certificateChain parameter. The first certificate in the list should
@@ -578,19 +754,25 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
the specified host name.
Note that the root (CA) certificate should not be included in the list to be verified,
- this will be looked up automatically either using the CA list specified by
- QSslSocket::defaultCaCertificates() or, if possible, it will be loaded on demand
- on Unix.
+ this will be looked up automatically using the CA list specified in the
+ default QSslConfiguration, and, in addition, if possible, CA certificates loaded on
+ demand on Unix and Windows.
\since 5.0
*/
-#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
-#else
-QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
-#endif
{
- return QSslSocketBackendPrivate::verify(certificateChain, hostName);
+ const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend is available");
+ return {};
+ }
+ auto verifyPtr = tlsBackend->X509Verifier();
+ if (!verifyPtr) {
+ qCWarning(lcSsl, "Available TLS backend does not support manual certificate verification");
+ return {};
+ }
+ return verifyPtr(certificateChain, hostName);
}
/*!
@@ -610,10 +792,43 @@ bool QSslCertificate::importPkcs12(QIODevice *device,
QList<QSslCertificate> *caCertificates,
const QByteArray &passPhrase)
{
- return QSslSocketBackendPrivate::importPkcs12(device, key, certificate, caCertificates, passPhrase);
+ if (!device || !key || !certificate)
+ return false;
+
+ const auto *tlsBackend = QTlsBackend::activeOrAnyBackend();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend is available");
+ return false;
+ }
+
+ if (auto reader = tlsBackend->X509Pkcs12Reader())
+ return reader(device, key, certificate, caCertificates, passPhrase);
+
+ qCWarning(lcSsl, "Available TLS backend does not support PKCS12");
+
+ return false;
}
+#endif // QT_NO_SSL
-#endif
+QList<QSslCertificateExtension> QSslCertificatePrivate::extensions() const
+{
+ QList<QSslCertificateExtension> result;
+
+ if (backend.get()) {
+ auto nExt = backend->numberOfExtensions();
+ for (decltype (nExt) i = 0; i < nExt; ++i) {
+ QSslCertificateExtension ext;
+ ext.d->oid = backend->oidForExtension(i);
+ ext.d->name = backend->nameForExtension(i);
+ ext.d->value = backend->valueForExtension(i);
+ ext.d->critical = backend->isExtensionCritical(i);
+ ext.d->supported = backend->isExtensionSupported(i);
+ result << ext;
+ }
+ }
+
+ return result;
+}
// These certificates are known to be fraudulent and were created during the comodo
// compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
@@ -668,7 +883,7 @@ static const char *const certificate_blacklist[] = {
bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
{
for (int a = 0; certificate_blacklist[a] != nullptr; a++) {
- QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]);
+ auto blacklistedCommonName = QAnyStringView(QUtf8StringView(certificate_blacklist[(a+1)]));
if (certificate.serialNumber() == certificate_blacklist[a++] &&
(certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) ||
certificate.issuerInfo(QSslCertificate::CommonName).contains(blacklistedCommonName)))
@@ -679,19 +894,18 @@ bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectInfo info)
{
- QByteArray str;
switch (info) {
- case QSslCertificate::Organization: str = QByteArray("O"); break;
- case QSslCertificate::CommonName: str = QByteArray("CN"); break;
- case QSslCertificate::LocalityName: str = QByteArray("L"); break;
- case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break;
- case QSslCertificate::CountryName: str = QByteArray("C"); break;
- case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break;
- case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break;
- case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break;
- case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break;
+ case QSslCertificate::Organization: return "O"_ba;
+ case QSslCertificate::CommonName: return "CN"_ba;
+ case QSslCertificate::LocalityName: return"L"_ba;
+ case QSslCertificate::OrganizationalUnitName: return "OU"_ba;
+ case QSslCertificate::CountryName: return "C"_ba;
+ case QSslCertificate::StateOrProvinceName: return "ST"_ba;
+ case QSslCertificate::DistinguishedNameQualifier: return "dnQualifier"_ba;
+ case QSslCertificate::SerialNumber: return "serialNumber"_ba;
+ case QSslCertificate::EmailAddress: return "emailAddress"_ba;
}
- return str;
+ return QByteArray();
}
/*!
@@ -708,13 +922,13 @@ QString QSslCertificate::issuerDisplayName() const
QStringList names;
names = issuerInfo(QSslCertificate::CommonName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = issuerInfo(QSslCertificate::Organization);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = issuerInfo(QSslCertificate::OrganizationalUnitName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
return QString();
}
@@ -733,24 +947,30 @@ QString QSslCertificate::subjectDisplayName() const
QStringList names;
names = subjectInfo(QSslCertificate::CommonName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = subjectInfo(QSslCertificate::Organization);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = subjectInfo(QSslCertificate::OrganizationalUnitName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
return QString();
}
/*!
- \fn uint qHash(const QSslCertificate &key, uint seed)
-
Returns the hash value for the \a key, using \a seed to seed the calculation.
\since 5.4
\relates QHash
*/
+size_t qHash(const QSslCertificate &key, size_t seed) noexcept
+{
+ if (const auto *backend = key.d->backend.get())
+ return backend->hash(seed);
+
+ return seed;
+
+}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
@@ -758,15 +978,15 @@ QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
QDebugStateSaver saver(debug);
debug.resetFormat().nospace();
debug << "QSslCertificate("
- << certificate.version()
- << ", " << certificate.serialNumber()
- << ", " << certificate.digest().toBase64()
- << ", " << certificate.issuerDisplayName()
- << ", " << certificate.subjectDisplayName()
- << ", " << certificate.subjectAlternativeNames()
+ << "Version=" << certificate.version()
+ << ", SerialNumber=" << certificate.serialNumber()
+ << ", Digest=" << certificate.digest().toBase64()
+ << ", Issuer=" << certificate.issuerDisplayName()
+ << ", Subject=" << certificate.subjectDisplayName()
+ << ", AlternativeSubjectNames=" << certificate.subjectAlternativeNames()
#if QT_CONFIG(datestring)
- << ", " << certificate.effectiveDate()
- << ", " << certificate.expiryDate()
+ << ", EffectiveDate=" << certificate.effectiveDate()
+ << ", ExpiryDate=" << certificate.expiryDate()
#endif
<< ')';
return debug;
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 69901b526c..cdf11b28b0 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLCERTIFICATE_H
@@ -50,9 +14,8 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdatetime.h>
-#include <QtCore/qregexp.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qmap.h>
+#include <QtCore/qshareddata.h>
#include <QtNetwork/qssl.h>
QT_BEGIN_NAMESPACE
@@ -62,11 +25,10 @@ class QIODevice;
class QSslError;
class QSslKey;
class QSslCertificateExtension;
-class QStringList;
class QSslCertificate;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed = 0) noexcept;
class QSslCertificatePrivate;
class Q_NETWORK_EXPORT QSslCertificate
@@ -84,6 +46,13 @@ public:
EmailAddress
};
+ enum class PatternSyntax {
+ RegularExpression,
+ Wildcard,
+ FixedString
+ };
+
+
explicit QSslCertificate(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
explicit QSslCertificate(const QByteArray &data = QByteArray(), QSsl::EncodingFormat format = QSsl::Pem);
QSslCertificate(const QSslCertificate &other);
@@ -92,20 +61,12 @@ public:
QSslCertificate &operator=(const QSslCertificate &other);
void swap(QSslCertificate &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
bool operator==(const QSslCertificate &other) const;
inline bool operator!=(const QSslCertificate &other) const { return !operator==(other); }
bool isNull() const;
-#if QT_DEPRECATED_SINCE(5,0)
- QT_DEPRECATED inline bool isValid() const {
- const QDateTime currentTime = QDateTime::currentDateTimeUtc();
- return currentTime >= effectiveDate() &&
- currentTime <= expiryDate() &&
- !isBlacklisted();
- }
-#endif
bool isBlacklisted() const;
bool isSelfSigned() const;
void clear();
@@ -123,10 +84,6 @@ public:
QList<QByteArray> subjectInfoAttributes() const;
QList<QByteArray> issuerInfoAttributes() const;
-#if QT_DEPRECATED_SINCE(5,0)
- QT_DEPRECATED inline QMultiMap<QSsl::AlternateNameEntryType, QString>
- alternateSubjectNames() const { return subjectAlternativeNames(); }
-#endif
QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const;
QDateTime effectiveDate() const;
QDateTime expiryDate() const;
@@ -139,21 +96,17 @@ public:
QByteArray toDer() const;
QString toText() const;
- static QList<QSslCertificate> fromPath(
- const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ static QList<QSslCertificate> fromPath(const QString &path,
+ QSsl::EncodingFormat format = QSsl::Pem,
+ PatternSyntax syntax = PatternSyntax::FixedString);
+
static QList<QSslCertificate> fromDevice(
QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
static QList<QSslCertificate> fromData(
const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
#ifndef QT_NO_SSL
-#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString());
-#else
- static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName = QString());
-#endif
-
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates = nullptr,
@@ -164,10 +117,9 @@ public:
private:
QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
- friend class QSslCertificatePrivate;
- friend class QSslSocketBackendPrivate;
+ friend class QTlsBackend;
- friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QSslCertificate &key, size_t seed) noexcept;
};
Q_DECLARE_SHARED(QSslCertificate)
@@ -179,6 +131,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo in
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSslCertificate)
+QT_DECL_METATYPE_EXTERN(QSslCertificate, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
deleted file mode 100644
index 6f1fb26add..0000000000
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ /dev/null
@@ -1,763 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qssl_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslcertificate_p.h"
-#include "qsslkey_p.h"
-#include "qsslcertificateextension_p.h"
-
-#include <QtCore/qendian.h>
-#include <QtCore/qmutex.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_CONSTEXPR int MutexPoolSize = 17;
-static QBasicMutex mutexPool[MutexPoolSize];
-namespace QMutexPool {
- static QBasicMutex *globalInstanceGet(const void *addr)
- {
- return mutexPool + (quintptr(addr) % MutexPoolSize);
- }
-}
-
-// forward declaration
-static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name);
-
-bool QSslCertificate::operator==(const QSslCertificate &other) const
-{
- if (d == other.d)
- return true;
- if (d->null && other.d->null)
- return true;
- if (d->x509 && other.d->x509)
- return q_X509_cmp(d->x509, other.d->x509) == 0;
- return false;
-}
-
-uint qHash(const QSslCertificate &key, uint seed) noexcept
-{
- if (X509 * const x509 = key.d->x509) {
- const EVP_MD *sha1 = q_EVP_sha1();
- unsigned int len = 0;
- unsigned char md[EVP_MAX_MD_SIZE];
- q_X509_digest(x509, sha1, md, &len);
- return qHashBits(md, len, seed);
- }
-
- return seed;
-}
-
-bool QSslCertificate::isNull() const
-{
- return d->null;
-}
-
-bool QSslCertificate::isSelfSigned() const
-{
- if (!d->x509)
- return false;
-
- return (q_X509_check_issued(d->x509, d->x509) == X509_V_OK);
-}
-
-QByteArray QSslCertificate::version() const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- if (d->versionString.isEmpty() && d->x509)
- d->versionString = QByteArray::number(qlonglong(q_X509_get_version(d->x509)) + 1);
-
- return d->versionString;
-}
-
-QByteArray QSslCertificate::serialNumber() const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- if (d->serialNumberString.isEmpty() && d->x509) {
- ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(d->x509);
- QByteArray hexString;
- hexString.reserve(serialNumber->length * 3);
- for (int a = 0; a < serialNumber->length; ++a) {
- hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
- hexString += ':';
- }
- hexString.chop(1);
- d->serialNumberString = hexString;
- }
- return d->serialNumberString;
-}
-
-QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->issuerInfo.isEmpty() && d->x509)
- d->issuerInfo =
- _q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
-
- return d->issuerInfo.values(d->subjectInfoToString(info));
-}
-
-QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->issuerInfo.isEmpty() && d->x509)
- d->issuerInfo =
- _q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
-
- return d->issuerInfo.values(attribute);
-}
-
-QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->subjectInfo.isEmpty() && d->x509)
- d->subjectInfo =
- _q_mapFromX509Name(q_X509_get_subject_name(d->x509));
-
- return d->subjectInfo.values(d->subjectInfoToString(info));
-}
-
-QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->subjectInfo.isEmpty() && d->x509)
- d->subjectInfo =
- _q_mapFromX509Name(q_X509_get_subject_name(d->x509));
-
- return d->subjectInfo.values(attribute);
-}
-
-QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->subjectInfo.isEmpty() && d->x509)
- d->subjectInfo =
- _q_mapFromX509Name(q_X509_get_subject_name(d->x509));
-
- return d->subjectInfo.uniqueKeys();
-}
-
-QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
-{
-#if QT_CONFIG(thread)
- QMutexLocker lock(QMutexPool::globalInstanceGet(d.data()));
-#endif
- // lazy init
- if (d->issuerInfo.isEmpty() && d->x509)
- d->issuerInfo =
- _q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
-
- return d->issuerInfo.uniqueKeys();
-}
-
-QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
-{
- QMultiMap<QSsl::AlternativeNameEntryType, QString> result;
-
- if (!d->x509)
- return result;
-
- STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i(
- d->x509, NID_subject_alt_name, nullptr, nullptr);
-
- auto altName = [](ASN1_IA5STRING *ia5, int len) {
- const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(ia5));
- return QString::fromLatin1(altNameStr, len);
- };
- if (altNames) {
- for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
- const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
- if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD)
- continue;
-
- int len = q_ASN1_STRING_length(genName->d.ia5);
- if (len < 0 || len >= 8192) {
- // broken name
- continue;
- }
-
- switch (genName->type) {
- case GEN_DNS:
- result.insert(QSsl::DnsEntry, altName(genName->d.ia5, len));
- break;
- case GEN_EMAIL:
- result.insert(QSsl::EmailEntry, altName(genName->d.ia5, len));
- break;
- case GEN_IPADD: {
- QHostAddress ipAddress;
- switch (len) {
- case 4: // IPv4
- ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(genName->d.iPAddress->data)));
- break;
- case 16: // IPv6
- ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data));
- break;
- default: // Unknown IP address format
- break;
- }
- if (!ipAddress.isNull())
- result.insert(QSsl::IpAddressEntry, ipAddress.toString());
- break;
- }
- default:
- break;
- }
- }
-
- q_OPENSSL_sk_pop_free((OPENSSL_STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free));
- }
-
- return result;
-}
-
-QDateTime QSslCertificate::effectiveDate() const
-{
- return d->notValidBefore;
-}
-
-QDateTime QSslCertificate::expiryDate() const
-{
- return d->notValidAfter;
-}
-
-Qt::HANDLE QSslCertificate::handle() const
-{
- return Qt::HANDLE(d->x509);
-}
-
-QSslKey QSslCertificate::publicKey() const
-{
- if (!d->x509)
- return QSslKey();
-
- QSslKey key;
-
- key.d->type = QSsl::PublicKey;
-
- EVP_PKEY *pkey = q_X509_get_pubkey(d->x509);
- Q_ASSERT(pkey);
- const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
-
- if (keyType == EVP_PKEY_RSA) {
- key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
- key.d->algorithm = QSsl::Rsa;
- key.d->isNull = false;
- } else if (keyType == EVP_PKEY_DSA) {
- key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
- key.d->algorithm = QSsl::Dsa;
- key.d->isNull = false;
-#ifndef OPENSSL_NO_EC
- } else if (keyType == EVP_PKEY_EC) {
- key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
- key.d->algorithm = QSsl::Ec;
- key.d->isNull = false;
-#endif
- } else if (keyType == EVP_PKEY_DH) {
- // DH unsupported
- } else {
- // error?
- }
-
- q_EVP_PKEY_free(pkey);
- return key;
-}
-
-/*
- * Convert unknown extensions to a QVariant.
- */
-static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
-{
- // Get the extension specific method object if available
- // we cast away the const-ness here because some versions of openssl
- // don't use const for the parameters in the functions pointers stored
- // in the object.
- X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
- if (!meth) {
- ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(ext);
- QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(value)),
- q_ASN1_STRING_length(value));
- return result;
- }
-
- //const unsigned char *data = ext->value->data;
- void *ext_internal = q_X509V3_EXT_d2i(ext);
-
- // If this extension can be converted
- if (meth->i2v && ext_internal) {
- STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
-
- QVariantMap map;
- QVariantList list;
- bool isMap = false;
-
- for (int j = 0; j < q_SKM_sk_num(CONF_VALUE, val); j++) {
- CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j);
- if (nval->name && nval->value) {
- isMap = true;
- map[QString::fromUtf8(nval->name)] = QString::fromUtf8(nval->value);
- } else if (nval->name) {
- list << QString::fromUtf8(nval->name);
- } else if (nval->value) {
- list << QString::fromUtf8(nval->value);
- }
- }
-
- if (isMap)
- return map;
- else
- return list;
- } else if (meth->i2s && ext_internal) {
- //qCDebug(lcSsl) << meth->i2s(meth, ext_internal);
- QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal)));
- return result;
- } else if (meth->i2r && ext_internal) {
- QByteArray result;
-
- BIO *bio = q_BIO_new(q_BIO_s_mem());
- if (!bio)
- return result;
-
- meth->i2r(meth, ext_internal, bio, 0);
-
- char *bio_buffer;
- long bio_size = q_BIO_get_mem_data(bio, &bio_buffer);
- result = QByteArray(bio_buffer, bio_size);
-
- q_BIO_free(bio);
- return result;
- }
-
- return QVariant();
-}
-
-/*
- * Convert extensions to a variant. The naming of the keys of the map are
- * taken from RFC 5280, however we decided the capitalisation in the RFC
- * was too silly for the real world.
- */
-static QVariant x509ExtensionToValue(X509_EXTENSION *ext)
-{
- ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
- int nid = q_OBJ_obj2nid(obj);
-
- switch (nid) {
- case NID_basic_constraints:
- {
- BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(ext));
-
- QVariantMap result;
- result[QLatin1String("ca")] = basic->ca ? true : false;
- if (basic->pathlen)
- result[QLatin1String("pathLenConstraint")] = (qlonglong)q_ASN1_INTEGER_get(basic->pathlen);
-
- q_BASIC_CONSTRAINTS_free(basic);
- return result;
- }
- break;
- case NID_info_access:
- {
- AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(ext));
-
- QVariantMap result;
- for (int i=0; i < q_SKM_sk_num(ACCESS_DESCRIPTION, info); i++) {
- ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i);
-
- GENERAL_NAME *name = ad->location;
- if (name->type == GEN_URI) {
- int len = q_ASN1_STRING_length(name->d.uniformResourceIdentifier);
- if (len < 0 || len >= 8192) {
- // broken name
- continue;
- }
-
- const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(name->d.uniformResourceIdentifier));
- const QString uri = QString::fromUtf8(uriStr, len);
-
- result[QString::fromUtf8(QSslCertificatePrivate::asn1ObjectName(ad->method))] = uri;
- } else {
- qCWarning(lcSsl) << "Strange location type" << name->type;
- }
- }
-
- q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<void(*)(void *)>(q_OPENSSL_sk_free));
- return result;
- }
- break;
- case NID_subject_key_identifier:
- {
- void *ext_internal = q_X509V3_EXT_d2i(ext);
-
- // we cast away the const-ness here because some versions of openssl
- // don't use const for the parameters in the functions pointers stored
- // in the object.
- X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
-
- return QVariant(QString::fromUtf8(meth->i2s(meth, ext_internal)));
- }
- break;
- case NID_authority_key_identifier:
- {
- AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(ext));
-
- QVariantMap result;
-
- // keyid
- if (auth_key->keyid) {
- QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data),
- auth_key->keyid->length);
- result[QLatin1String("keyid")] = keyid.toHex();
- }
-
- // issuer
- // TODO: GENERAL_NAMES
-
- // serial
- if (auth_key->serial)
- result[QLatin1String("serial")] = (qlonglong)q_ASN1_INTEGER_get(auth_key->serial);
-
- q_AUTHORITY_KEYID_free(auth_key);
- return result;
- }
- break;
- }
-
- return QVariant();
-}
-
-QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext)
-{
- QSslCertificateExtension result;
-
- ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
- QByteArray oid = QSslCertificatePrivate::asn1ObjectId(obj);
- QByteArray name = QSslCertificatePrivate::asn1ObjectName(obj);
-
- result.d->oid = QString::fromUtf8(oid);
- result.d->name = QString::fromUtf8(name);
-
- bool critical = q_X509_EXTENSION_get_critical(ext);
- result.d->critical = critical;
-
- // Lets see if we have custom support for this one
- QVariant extensionValue = x509ExtensionToValue(ext);
- if (extensionValue.isValid()) {
- result.d->value = extensionValue;
- result.d->supported = true;
-
- return result;
- }
-
- extensionValue = x509UnknownExtensionToValue(ext);
- if (extensionValue.isValid()) {
- result.d->value = extensionValue;
- result.d->supported = false;
- return result;
- }
-
- return result;
-}
-
-QList<QSslCertificateExtension> QSslCertificate::extensions() const
-{
- QList<QSslCertificateExtension> result;
-
- if (!d->x509)
- return result;
-
- int count = q_X509_get_ext_count(d->x509);
- result.reserve(count);
-
- for (int i = 0; i < count; i++) {
- X509_EXTENSION *ext = q_X509_get_ext(d->x509, i);
- result << QSslCertificatePrivate::convertExtension(ext);
- }
-
- return result;
-}
-
-QByteArray QSslCertificate::toPem() const
-{
- if (!d->x509)
- return QByteArray();
- return d->QByteArray_from_X509(d->x509, QSsl::Pem);
-}
-
-QByteArray QSslCertificate::toDer() const
-{
- if (!d->x509)
- return QByteArray();
- return d->QByteArray_from_X509(d->x509, QSsl::Der);
-}
-
-QString QSslCertificate::toText() const
-{
- if (!d->x509)
- return QString();
- return d->text_from_X509(d->x509);
-}
-
-#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
-#define ENDCERTSTRING "-----END CERTIFICATE-----"
-
-void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
-{
- if (!data.isEmpty()) {
- const QList<QSslCertificate> certs = (format == QSsl::Pem)
- ? certificatesFromPem(data, 1)
- : certificatesFromDer(data, 1);
- if (!certs.isEmpty()) {
- *this = *certs.first().d;
- if (x509)
- x509 = q_X509_dup(x509);
- }
- }
-}
-
-// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
-QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
-{
- if (!x509) {
- qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
- return QByteArray();
- }
-
- // Use i2d_X509 to convert the X509 to an array.
- int length = q_i2d_X509(x509, nullptr);
- QByteArray array;
- array.resize(length);
- char *data = array.data();
- char **dataP = &data;
- unsigned char **dataPu = (unsigned char **)dataP;
- if (q_i2d_X509(x509, dataPu) < 0)
- return QByteArray();
-
- if (format == QSsl::Der)
- return array;
-
- // Convert to Base64 - wrap at 64 characters.
- array = array.toBase64();
- QByteArray tmp;
- for (int i = 0; i <= array.size() - 64; i += 64) {
- tmp += QByteArray::fromRawData(array.data() + i, 64);
- tmp += '\n';
- }
- if (int remainder = array.size() % 64) {
- tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
- tmp += '\n';
- }
-
- return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
-}
-
-QString QSslCertificatePrivate::text_from_X509(X509 *x509)
-{
- if (!x509) {
- qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509");
- return QString();
- }
-
- QByteArray result;
- BIO *bio = q_BIO_new(q_BIO_s_mem());
- if (!bio)
- return QString();
-
- q_X509_print(bio, x509);
-
- QVarLengthArray<char, 16384> data;
- int count = q_BIO_read(bio, data.data(), 16384);
- if ( count > 0 ) {
- result = QByteArray( data.data(), count );
- }
-
- q_BIO_free(bio);
-
- return QString::fromLatin1(result);
-}
-
-QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object)
-{
- char buf[80]; // The openssl docs a buffer length of 80 should be more than enough
- q_OBJ_obj2txt(buf, sizeof(buf), object, 1); // the 1 says always use the oid not the long name
-
- return QByteArray(buf);
-}
-
-
-QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object)
-{
- int nid = q_OBJ_obj2nid(object);
- if (nid != NID_undef)
- return QByteArray(q_OBJ_nid2sn(nid));
-
- return asn1ObjectId(object);
-}
-
-static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name)
-{
- QMultiMap<QByteArray, QString> info;
- for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
- X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
-
- QByteArray name = QSslCertificatePrivate::asn1ObjectName(q_X509_NAME_ENTRY_get_object(e));
- unsigned char *data = nullptr;
- int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
- info.insert(name, QString::fromUtf8((char*)data, size));
-#if QT_CONFIG(opensslv11)
- q_CRYPTO_free(data, nullptr, 0);
-#else
- q_CRYPTO_free(data);
-#endif
- }
-
- return info;
-}
-
-QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
-{
- QSslCertificate certificate;
- if (!x509 || !QSslSocket::supportsSsl())
- return certificate;
-
- ASN1_TIME *nbef = q_X509_getm_notBefore(x509);
- ASN1_TIME *naft = q_X509_getm_notAfter(x509);
-
- certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
- certificate.d->notValidAfter = q_getTimeFromASN1(naft);
- certificate.d->null = false;
- certificate.d->x509 = q_X509_dup(x509);
-
- return certificate;
-}
-
-static bool matchLineFeed(const QByteArray &pem, int *offset)
-{
- char ch = 0;
-
- // ignore extra whitespace at the end of the line
- while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
- ++*offset;
-
- if (ch == '\n') {
- *offset += 1;
- return true;
- }
- if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
- *offset += 2;
- return true;
- }
- return false;
-}
-
-QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
-{
- QList<QSslCertificate> certificates;
- QSslSocketPrivate::ensureInitialized();
-
- int offset = 0;
- while (count == -1 || certificates.size() < count) {
- int startPos = pem.indexOf(BEGINCERTSTRING, offset);
- if (startPos == -1)
- break;
- startPos += sizeof(BEGINCERTSTRING) - 1;
- if (!matchLineFeed(pem, &startPos))
- break;
-
- int endPos = pem.indexOf(ENDCERTSTRING, startPos);
- if (endPos == -1)
- break;
-
- offset = endPos + sizeof(ENDCERTSTRING) - 1;
- if (offset < pem.size() && !matchLineFeed(pem, &offset))
- break;
-
- QByteArray decoded = QByteArray::fromBase64(
- QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
- const unsigned char *data = (const unsigned char *)decoded.data();
-
- if (X509 *x509 = q_d2i_X509(nullptr, &data, decoded.size())) {
- certificates << QSslCertificate_from_X509(x509);
- q_X509_free(x509);
- }
- }
-
- return certificates;
-}
-
-QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
-{
- QList<QSslCertificate> certificates;
- QSslSocketPrivate::ensureInitialized();
-
- const unsigned char *data = (const unsigned char *)der.data();
- int size = der.size();
-
- while (size > 0 && (count == -1 || certificates.size() < count)) {
- if (X509 *x509 = q_d2i_X509(nullptr, &data, size)) {
- certificates << QSslCertificate_from_X509(x509);
- q_X509_free(x509);
- } else {
- break;
- }
- size -= ((const char *)data - der.data());
- }
-
- return certificates;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index 234cd45ceb..ca59abae82 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -1,48 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QSSLCERTIFICATE_OPENSSL_P_H
-#define QSSLCERTIFICATE_OPENSSL_P_H
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qsslcertificate.h"
+#ifndef QSSLCERTIFICATE_P_H
+#define QSSLCERTIFICATE_P_H
//
// W A R N I N G
@@ -55,110 +16,32 @@
// We mean it.
//
-#ifndef QT_NO_SSL
-#include "qsslsocket_p.h"
-#endif
-#include "qsslcertificateextension.h"
-#include <QtCore/qdatetime.h>
-#include <QtCore/qmap.h>
+#include <QtNetwork/private/qtnetworkglobal_p.h>
-#ifndef QT_NO_OPENSSL
-#include <openssl/x509.h>
-#else
-struct X509;
-struct X509_EXTENSION;
-struct ASN1_OBJECT;
-#endif
+#include "qsslcertificateextension.h"
+#include "qsslcertificate.h"
+#include "qtlsbackend_p.h"
-#ifdef Q_OS_WINRT
-#include <wrl.h>
-#include <windows.security.cryptography.certificates.h>
-#endif
+#include <qlist.h>
-#if QT_CONFIG(schannel)
-#include <wincrypt.h>
-#endif
+#include <memory>
QT_BEGIN_NAMESPACE
-// forward declaration
-
class QSslCertificatePrivate
{
public:
- QSslCertificatePrivate()
- : null(true), x509(nullptr)
- {
-#ifndef QT_NO_SSL
- QSslSocketPrivate::ensureInitialized();
-#endif
- }
-
- ~QSslCertificatePrivate()
- {
-#ifndef QT_NO_OPENSSL
- if (x509)
- q_X509_free(x509);
-#endif
-#if QT_CONFIG(schannel)
- if (certificateContext)
- CertFreeCertificateContext(certificateContext);
-#endif
- }
-
- bool null;
- QByteArray versionString;
- QByteArray serialNumberString;
+ QSslCertificatePrivate();
+ ~QSslCertificatePrivate();
- QMultiMap<QByteArray, QString> issuerInfo;
- QMultiMap<QByteArray, QString> subjectInfo;
- QDateTime notValidAfter;
- QDateTime notValidBefore;
-
-#ifdef QT_NO_OPENSSL
- bool subjectMatchesIssuer;
- QSsl::KeyAlgorithm publicKeyAlgorithm;
- QByteArray publicKeyDerData;
- QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames;
- QList<QSslCertificateExtension> extensions;
-
- QByteArray derData;
-
- bool parse(const QByteArray &data);
- bool parseExtension(const QByteArray &data, QSslCertificateExtension *extension);
-#endif
- X509 *x509;
-
- void init(const QByteArray &data, QSsl::EncodingFormat format);
-
- static QByteArray asn1ObjectId(ASN1_OBJECT *object);
- static QByteArray asn1ObjectName(ASN1_OBJECT *object);
- static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format);
- static QString text_from_X509(X509 *x509);
- static QSslCertificate QSslCertificate_from_X509(X509 *x509);
- static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count = -1);
- static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count = -1);
- static bool isBlacklisted(const QSslCertificate &certificate);
- static QSslCertificateExtension convertExtension(X509_EXTENSION *ext);
- static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
-
- friend class QSslSocketBackendPrivate;
+ QList<QSslCertificateExtension> extensions() const;
+ Q_NETWORK_EXPORT static bool isBlacklisted(const QSslCertificate &certificate);
+ Q_NETWORK_EXPORT static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
QAtomicInt ref;
-
-#ifdef Q_OS_WINRT
- Microsoft::WRL::ComPtr<ABI::Windows::Security::Cryptography::Certificates::ICertificate> certificate;
-
- static QSslCertificate QSslCertificate_from_Certificate(ABI::Windows::Security::Cryptography::Certificates::ICertificate *iCertificate);
-#endif
-
-#if QT_CONFIG(schannel)
- const CERT_CONTEXT *certificateContext = nullptr;
-
- static QSslCertificate QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext);
-#endif
+ std::unique_ptr<QTlsPrivate::X509Certificate> backend;
};
QT_END_NAMESPACE
-#endif // QSSLCERTIFICATE_OPENSSL_P_H
+#endif // QSSLCERTIFICATE_P_H
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
deleted file mode 100644
index 8b5035ad96..0000000000
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ /dev/null
@@ -1,551 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslcertificate.h"
-#include "qsslcertificate_p.h"
-
-#include "qssl_p.h"
-#ifndef QT_NO_SSL
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#endif
-#include "qsslcertificateextension.h"
-#include "qsslcertificateextension_p.h"
-#include "qasn1element_p.h"
-
-#include <QtCore/qdatastream.h>
-#include <QtCore/qendian.h>
-#include <QtNetwork/qhostaddress.h>
-
-QT_BEGIN_NAMESPACE
-
-bool QSslCertificate::operator==(const QSslCertificate &other) const
-{
- if (d == other.d)
- return true;
- if (d->null && other.d->null)
- return true;
- return d->derData == other.d->derData;
-}
-
-uint qHash(const QSslCertificate &key, uint seed) noexcept
-{
- // DER is the native encoding here, so toDer() is just "return d->derData":
- return qHash(key.toDer(), seed);
-}
-
-bool QSslCertificate::isNull() const
-{
- return d->null;
-}
-
-bool QSslCertificate::isSelfSigned() const
-{
- if (d->null)
- return false;
-
- qCWarning(lcSsl,
- "QSslCertificate::isSelfSigned: This function does not check, whether the certificate "
- "is actually signed. It just checks whether issuer and subject are identical");
- return d->subjectMatchesIssuer;
-}
-
-QByteArray QSslCertificate::version() const
-{
- return d->versionString;
-}
-
-QByteArray QSslCertificate::serialNumber() const
-{
- return d->serialNumberString;
-}
-
-QStringList QSslCertificate::issuerInfo(SubjectInfo info) const
-{
- return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info));
-}
-
-QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const
-{
- return d->issuerInfo.values(attribute);
-}
-
-QStringList QSslCertificate::subjectInfo(SubjectInfo info) const
-{
- return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info));
-}
-
-QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const
-{
- return d->subjectInfo.values(attribute);
-}
-
-QList<QByteArray> QSslCertificate::subjectInfoAttributes() const
-{
- return d->subjectInfo.uniqueKeys();
-}
-
-QList<QByteArray> QSslCertificate::issuerInfoAttributes() const
-{
- return d->issuerInfo.uniqueKeys();
-}
-
-QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const
-{
- return d->subjectAlternativeNames;
-}
-
-QDateTime QSslCertificate::effectiveDate() const
-{
- return d->notValidBefore;
-}
-
-QDateTime QSslCertificate::expiryDate() const
-{
- return d->notValidAfter;
-}
-
-#if !defined(Q_OS_WINRT) && !QT_CONFIG(schannel) // implemented in qsslcertificate_{winrt,schannel}.cpp
-Qt::HANDLE QSslCertificate::handle() const
-{
- Q_UNIMPLEMENTED();
- return nullptr;
-}
-#endif
-
-#ifndef QT_NO_SSL
-QSslKey QSslCertificate::publicKey() const
-{
- QSslKey key;
- key.d->type = QSsl::PublicKey;
- if (d->publicKeyAlgorithm != QSsl::Opaque) {
- key.d->algorithm = d->publicKeyAlgorithm;
- key.d->decodeDer(d->publicKeyDerData);
- }
- return key;
-}
-#endif
-
-QList<QSslCertificateExtension> QSslCertificate::extensions() const
-{
- return d->extensions;
-}
-
-#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
-#define ENDCERTSTRING "-----END CERTIFICATE-----"
-
-QByteArray QSslCertificate::toPem() const
-{
- QByteArray array = toDer();
-
- // Convert to Base64 - wrap at 64 characters.
- array = array.toBase64();
- QByteArray tmp;
- for (int i = 0; i <= array.size() - 64; i += 64) {
- tmp += QByteArray::fromRawData(array.data() + i, 64);
- tmp += '\n';
- }
- if (int remainder = array.size() % 64) {
- tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
- tmp += '\n';
- }
-
- return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
-}
-
-QByteArray QSslCertificate::toDer() const
-{
- return d->derData;
-}
-
-QString QSslCertificate::toText() const
-{
- Q_UNIMPLEMENTED();
- return QString();
-}
-
-void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
-{
- if (!data.isEmpty()) {
- const QList<QSslCertificate> certs = (format == QSsl::Pem)
- ? certificatesFromPem(data, 1)
- : certificatesFromDer(data, 1);
- if (!certs.isEmpty()) {
- *this = *certs.first().d;
-#if QT_CONFIG(schannel)
- if (certificateContext)
- certificateContext = CertDuplicateCertificateContext(certificateContext);
-#endif
- }
- }
-}
-
-static bool matchLineFeed(const QByteArray &pem, int *offset)
-{
- char ch = 0;
-
- // ignore extra whitespace at the end of the line
- while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
- ++*offset;
-
- if (ch == '\n') {
- *offset += 1;
- return true;
- }
- if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
- *offset += 2;
- return true;
- }
- return false;
-}
-
-QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
-{
- QList<QSslCertificate> certificates;
- int offset = 0;
- while (count == -1 || certificates.size() < count) {
- int startPos = pem.indexOf(BEGINCERTSTRING, offset);
- if (startPos == -1)
- break;
- startPos += sizeof(BEGINCERTSTRING) - 1;
- if (!matchLineFeed(pem, &startPos))
- break;
-
- int endPos = pem.indexOf(ENDCERTSTRING, startPos);
- if (endPos == -1)
- break;
-
- offset = endPos + sizeof(ENDCERTSTRING) - 1;
- if (offset < pem.size() && !matchLineFeed(pem, &offset))
- break;
-
- QByteArray decoded = QByteArray::fromBase64(
- QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
- certificates << certificatesFromDer(decoded, 1);;
- }
-
- return certificates;
-}
-
-QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
-{
- QList<QSslCertificate> certificates;
-
- QByteArray data = der;
- while (count == -1 || certificates.size() < count) {
- QSslCertificate cert;
- if (!cert.d->parse(data))
- break;
-
- certificates << cert;
- data.remove(0, cert.d->derData.size());
- }
-
- return certificates;
-}
-
-static QByteArray colonSeparatedHex(const QByteArray &value)
-{
- const int size = value.size();
- int i = 0;
- while (i < size && !value.at(i)) // skip leading zeros
- ++i;
-
- return value.mid(i).toHex(':');
-}
-
-bool QSslCertificatePrivate::parse(const QByteArray &data)
-{
- QAsn1Element root;
-
- QDataStream dataStream(data);
- if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType)
- return false;
-
- QDataStream rootStream(root.value());
- QAsn1Element cert;
- if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType)
- return false;
-
- // version or serial number
- QAsn1Element elem;
- QDataStream certStream(cert.value());
- if (!elem.read(certStream))
- return false;
-
- if (elem.type() == QAsn1Element::Context0Type) {
- QDataStream versionStream(elem.value());
- if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType)
- return false;
-
- versionString = QByteArray::number(elem.value().at(0) + 1);
- if (!elem.read(certStream))
- return false;
- } else {
- versionString = QByteArray::number(1);
- }
-
- // serial number
- if (elem.type() != QAsn1Element::IntegerType)
- return false;
- serialNumberString = colonSeparatedHex(elem.value());
-
- // algorithm ID
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- // issuer info
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
- issuerInfo = elem.toInfo();
-
- // validity period
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QDataStream validityStream(elem.value());
- if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
- return false;
-
- notValidBefore = elem.toDateTime();
- if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
- return false;
-
- notValidAfter = elem.toDateTime();
-
- // subject name
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
- subjectInfo = elem.toInfo();
- subjectMatchesIssuer = issuerDer == subjectDer;
-
- // public key
- qint64 keyStart = certStream.device()->pos();
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- publicKeyDerData.resize(certStream.device()->pos() - keyStart);
- QDataStream keyStream(elem.value());
- if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
-
- // key algorithm
- if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
- return false;
-
- const QByteArray oid = elem.toObjectId();
- if (oid == RSA_ENCRYPTION_OID)
- publicKeyAlgorithm = QSsl::Rsa;
- else if (oid == DSA_ENCRYPTION_OID)
- publicKeyAlgorithm = QSsl::Dsa;
- else if (oid == EC_ENCRYPTION_OID)
- publicKeyAlgorithm = QSsl::Ec;
- else
- publicKeyAlgorithm = QSsl::Opaque;
-
- certStream.device()->seek(keyStart);
- certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
-
- // extensions
- while (elem.read(certStream)) {
- if (elem.type() == QAsn1Element::Context3Type) {
- if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
- QDataStream extStream(elem.value());
- while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
- QSslCertificateExtension extension;
- if (!parseExtension(elem.value(), &extension))
- return false;
- extensions << extension;
-
- if (extension.oid() == QLatin1String("2.5.29.17")) {
- // subjectAltName
- QAsn1Element sanElem;
- if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) {
- QDataStream nameStream(sanElem.value());
- QAsn1Element nameElem;
- while (nameElem.read(nameStream)) {
- switch (nameElem.type()) {
- case QAsn1Element::Rfc822NameType:
- subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
- break;
- case QAsn1Element::DnsNameType:
- subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
- break;
- case QAsn1Element::IpAddressType: {
- QHostAddress ipAddress;
- QByteArray ipAddrValue = nameElem.value();
- switch (ipAddrValue.length()) {
- case 4: // IPv4
- ipAddress = QHostAddress(qFromBigEndian(*reinterpret_cast<quint32 *>(ipAddrValue.data())));
- break;
- case 16: // IPv6
- ipAddress = QHostAddress(reinterpret_cast<quint8 *>(ipAddrValue.data()));
- break;
- default: // Unknown IP address format
- break;
- }
- if (!ipAddress.isNull())
- subjectAlternativeNames.insert(QSsl::IpAddressEntry, ipAddress.toString());
- break;
- }
- default:
- break;
- }
- }
- }
- }
- }
- }
- }
- }
-
- derData = data.left(dataStream.device()->pos());
- null = false;
- return true;
-}
-
-bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension)
-{
- bool ok;
- bool critical = false;
- QAsn1Element oidElem, valElem;
-
- QDataStream seqStream(data);
-
- // oid
- if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType)
- return false;
- const QByteArray oid = oidElem.toObjectId();
-
- // critical and value
- if (!valElem.read(seqStream))
- return false;
- if (valElem.type() == QAsn1Element::BooleanType) {
- critical = valElem.toBool(&ok);
- if (!ok || !valElem.read(seqStream))
- return false;
- }
- if (valElem.type() != QAsn1Element::OctetStringType)
- return false;
-
- // interpret value
- QAsn1Element val;
- bool supported = true;
- QVariant value;
- if (oid == "1.3.6.1.5.5.7.1.1") {
- // authorityInfoAccess
- if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
- return false;
- QVariantMap result;
- const auto elems = val.toVector();
- for (const QAsn1Element &el : elems) {
- QVector<QAsn1Element> items = el.toVector();
- if (items.size() != 2)
- return false;
- const QString key = QString::fromLatin1(items.at(0).toObjectName());
- switch (items.at(1).type()) {
- case QAsn1Element::Rfc822NameType:
- case QAsn1Element::DnsNameType:
- case QAsn1Element::UniformResourceIdentifierType:
- result[key] = items.at(1).toString();
- break;
- }
- }
- value = result;
- } else if (oid == "2.5.29.14") {
- // subjectKeyIdentifier
- if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType)
- return false;
- value = colonSeparatedHex(val.value()).toUpper();
- } else if (oid == "2.5.29.19") {
- // basicConstraints
- if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
- return false;
-
- QVariantMap result;
- QVector<QAsn1Element> items = val.toVector();
- if (items.size() > 0) {
- result[QStringLiteral("ca")] = items.at(0).toBool(&ok);
- if (!ok)
- return false;
- } else {
- result[QStringLiteral("ca")] = false;
- }
- if (items.size() > 1) {
- result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok);
- if (!ok)
- return false;
- }
- value = result;
- } else if (oid == "2.5.29.35") {
- // authorityKeyIdentifier
- if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
- return false;
- QVariantMap result;
- const auto elems = val.toVector();
- for (const QAsn1Element &el : elems) {
- if (el.type() == 0x80) {
- const QString key = QStringLiteral("keyid");
- result[key] = el.value().toHex();
- } else if (el.type() == 0x82) {
- const QString serial = QStringLiteral("serial");
- result[serial] = colonSeparatedHex(el.value());
- }
- }
- value = result;
- } else {
- supported = false;
- value = valElem.value();
- }
-
- extension->d->critical = critical;
- extension->d->supported = supported;
- extension->d->oid = QString::fromLatin1(oid);
- extension->d->name = QString::fromLatin1(oidElem.toObjectName());
- extension->d->value = value;
-
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate_schannel.cpp b/src/network/ssl/qsslcertificate_schannel.cpp
deleted file mode 100644
index 5ea713612a..0000000000
--- a/src/network/ssl/qsslcertificate_schannel.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslcertificate.h"
-#include "qsslcertificate_p.h"
-
-#include <wincrypt.h>
-
-QT_BEGIN_NAMESPACE
-
-QSslCertificate QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(const CERT_CONTEXT *certificateContext)
-{
- QByteArray derData = QByteArray((const char *)certificateContext->pbCertEncoded,
- certificateContext->cbCertEncoded);
-
- QSslCertificate certificate(derData, QSsl::Der);
- certificate.d->certificateContext = CertDuplicateCertificateContext(certificateContext);
- return certificate;
-}
-
-Qt::HANDLE QSslCertificate::handle() const
-{
- return Qt::HANDLE(d->certificateContext);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate_winrt.cpp b/src/network/ssl/qsslcertificate_winrt.cpp
deleted file mode 100644
index e601307c17..0000000000
--- a/src/network/ssl/qsslcertificate_winrt.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslcertificate_p.h"
-
-#include <QtCore/qfunctions_winrt.h>
-
-#include <wrl.h>
-#include <windows.storage.streams.h>
-#include <windows.security.cryptography.h>
-#include <robuffer.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Security::Cryptography;
-using namespace ABI::Windows::Security::Cryptography::Certificates;
-using namespace ABI::Windows::Storage::Streams;
-
-QT_USE_NAMESPACE
-
-struct SslCertificateGlobal
-{
- SslCertificateGlobal() {
- HRESULT hr;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Certificates_Certificate).Get(),
- &certificateFactory);
- Q_ASSERT_SUCCEEDED(hr);
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(),
- &bufferFactory);
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- ComPtr<ICertificateFactory> certificateFactory;
- ComPtr<ICryptographicBufferStatics> bufferFactory;
-};
-Q_GLOBAL_STATIC(SslCertificateGlobal, g)
-
-QSslCertificate QSslCertificatePrivate::QSslCertificate_from_Certificate(ICertificate *iCertificate)
-{
- Q_ASSERT(iCertificate);
- ComPtr<IBuffer> buffer;
- HRESULT hr = iCertificate->GetCertificateBlob(&buffer);
- RETURN_IF_FAILED("Could not obtain certification blob", return QSslCertificate());
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
- hr = buffer.As(&byteAccess);
- RETURN_IF_FAILED("Could not obtain byte access to buffer", return QSslCertificate());
- char *data;
- hr = byteAccess->Buffer(reinterpret_cast<byte **>(&data));
- RETURN_IF_FAILED("Could not obtain buffer data", return QSslCertificate());
- UINT32 size;
- hr = buffer->get_Length(&size);
- RETURN_IF_FAILED("Could not obtain buffer length ", return QSslCertificate());
- QByteArray der(data, size);
-
- QSslCertificate certificate;
- certificate.d->null = false;
- certificate.d->certificate = iCertificate;
-
- return certificatesFromDer(der, 1).at(0);
-}
-
-Qt::HANDLE QSslCertificate::handle() const
-{
- if (!d->certificate) {
- HRESULT hr;
- ComPtr<IBuffer> buffer;
- hr = g->bufferFactory->CreateFromByteArray(d->derData.length(), (BYTE *)d->derData.data(), &buffer);
- RETURN_IF_FAILED("Failed to create the certificate data buffer", return nullptr);
-
- hr = g->certificateFactory->CreateCertificate(buffer.Get(), &d->certificate);
- RETURN_IF_FAILED("Failed to create the certificate handle from the data buffer",
- return nullptr);
- }
-
- return d->certificate.Get();
-}
diff --git a/src/network/ssl/qsslcertificateextension.cpp b/src/network/ssl/qsslcertificateextension.cpp
index 4896d3909a..3f583e2e2f 100644
--- a/src/network/ssl/qsslcertificateextension.cpp
+++ b/src/network/ssl/qsslcertificateextension.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
\class QSslCertificateExtension
diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h
index 7cc8a888be..c639d2fa45 100644
--- a/src/network/ssl/qsslcertificateextension.h
+++ b/src/network/ssl/qsslcertificateextension.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLCERTIFICATEEXTENSION_H
#define QSSLCERTIFICATEEXTENSION_H
@@ -59,7 +23,7 @@ public:
QSslCertificateExtension &operator=(const QSslCertificateExtension &other);
~QSslCertificateExtension();
- void swap(QSslCertificateExtension &other) noexcept { qSwap(d, other.d); }
+ void swap(QSslCertificateExtension &other) noexcept { d.swap(other.d); }
QString oid() const;
QString name() const;
diff --git a/src/network/ssl/qsslcertificateextension_p.h b/src/network/ssl/qsslcertificateextension_p.h
index 373f92a5cf..3f5d1e373e 100644
--- a/src/network/ssl/qsslcertificateextension_p.h
+++ b/src/network/ssl/qsslcertificateextension_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2011 Richard J. Moore <rich@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLCERTIFICATEEXTENSION_P_H
#define QSSLCERTIFICATEEXTENSION_P_H
diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp
index 738d521a38..2a4da7991a 100644
--- a/src/network/ssl/qsslcipher.cpp
+++ b/src/network/ssl/qsslcipher.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -68,6 +32,9 @@
QT_BEGIN_NAMESPACE
+static_assert(QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ && sizeof(QScopedPointer<QSslCipherPrivate>) == sizeof(std::unique_ptr<QSslCipherPrivate>));
+
/*!
Constructs an empty QSslCipher object.
*/
@@ -127,7 +94,7 @@ QSslCipher::QSslCipher(const QString &name, QSsl::SslProtocol protocol)
QSslCipher::QSslCipher(const QSslCipher &other)
: d(new QSslCipherPrivate)
{
- *d.data() = *other.d.data();
+ *d.get() = *other.d.get();
}
/*!
@@ -143,7 +110,7 @@ QSslCipher::~QSslCipher()
*/
QSslCipher &QSslCipher::operator=(const QSslCipher &other)
{
- *d.data() = *other.d.data();
+ *d.get() = *other.d.get();
return *this;
}
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
index 6994f590ae..ed727947f5 100644
--- a/src/network/ssl/qsslcipher.h
+++ b/src/network/ssl/qsslcipher.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLCIPHER_H
@@ -46,6 +10,8 @@
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qssl.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
@@ -64,7 +30,7 @@ public:
~QSslCipher();
void swap(QSslCipher &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
bool operator==(const QSslCipher &other) const;
inline bool operator!=(const QSslCipher &other) const { return !operator==(other); }
@@ -81,8 +47,9 @@ public:
QSsl::SslProtocol protocol() const;
private:
- QScopedPointer<QSslCipherPrivate> d;
- friend class QSslSocketBackendPrivate;
+ // ### Qt 7: make implicitly shared
+ std::unique_ptr<QSslCipherPrivate> d;
+ friend class QTlsBackend;
};
Q_DECLARE_SHARED(QSslCipher)
diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h
index b8629f9f96..d7f5e7c471 100644
--- a/src/network/ssl/qsslcipher_p.h
+++ b/src/network/ssl/qsslcipher_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLCIPHER_P_H
#define QSSLCIPHER_P_H
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index a2e694ec92..fd308d7037 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qssl_p.h"
#include "qsslconfiguration.h"
@@ -48,13 +12,14 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QSslConfiguration)
+
const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOptionDisableEmptyFragments
|QSsl::SslOptionDisableLegacyRenegotiation
|QSsl::SslOptionDisableCompression
|QSsl::SslOptionDisableSessionPersistence;
const char QSslConfiguration::ALPNProtocolHTTP2[] = "h2";
-const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
/*!
@@ -108,7 +73,7 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
change the settings in the related SSL connection. You must call
setSslConfiguration on a modified QSslConfiguration object to
achieve that. The following example illustrates how to change the
- protocol to TLSv1_0 in a QSslSocket object:
+ protocol to TLSv1_2 in a QSslSocket object:
\snippet code/src_network_ssl_qsslconfiguration.cpp 0
@@ -134,14 +99,14 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
*/
/*!
- \variable QSslConfiguration::NextProtocolSpdy3_0
- \brief The value used for negotiating SPDY 3.0 during the Next
+ \variable QSslConfiguration::NextProtocolHttp1_1
+ \brief The value used for negotiating HTTP 1.1 during the Next
Protocol Negotiation.
*/
/*!
- \variable QSslConfiguration::NextProtocolHttp1_1
- \brief The value used for negotiating HTTP 1.1 during the Next
+ \variable QSslConfiguration::ALPNProtocolHTTP2
+ \brief The value used for negotiating HTTP 2 during the Application-Layer
Protocol Negotiation.
*/
@@ -229,7 +194,9 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->nextNegotiatedProtocol == other.d->nextNegotiatedProtocol &&
d->nextProtocolNegotiationStatus == other.d->nextProtocolNegotiationStatus &&
d->dtlsCookieEnabled == other.d->dtlsCookieEnabled &&
- d->ocspStaplingEnabled == other.d->ocspStaplingEnabled;
+ d->ocspStaplingEnabled == other.d->ocspStaplingEnabled &&
+ d->reportFromCallback == other.d->reportFromCallback &&
+ d->missingCertIsFatal == other.d->missingCertIsFatal;
}
/*!
@@ -257,15 +224,15 @@ bool QSslConfiguration::isNull() const
d->peerVerifyMode == QSslSocket::AutoVerifyPeer &&
d->peerVerifyDepth == 0 &&
d->allowRootCertOnDemandLoading == true &&
- d->caCertificates.count() == 0 &&
- d->ciphers.count() == 0 &&
+ d->caCertificates.size() == 0 &&
+ d->ciphers.size() == 0 &&
d->ellipticCurves.isEmpty() &&
d->ephemeralServerKey.isNull() &&
d->dhParams == QSslDiffieHellmanParameters::defaultParameters() &&
d->localCertificateChain.isEmpty() &&
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
- d->peerCertificateChain.count() == 0 &&
+ d->peerCertificateChain.size() == 0 &&
d->backendConfig.isEmpty() &&
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
@@ -274,7 +241,9 @@ bool QSslConfiguration::isNull() const
d->nextAllowedProtocols.isEmpty() &&
d->nextNegotiatedProtocol.isNull() &&
d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone &&
- d->ocspStaplingEnabled == false);
+ d->ocspStaplingEnabled == false &&
+ d->reportFromCallback == false &&
+ d->missingCertIsFatal == false);
}
/*!
@@ -521,7 +490,7 @@ QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const
eventually select the session cipher. This ordered list must be in
place before the handshake phase begins.
- \sa ciphers(), setCiphers(), QSslSocket::supportedCiphers()
+ \sa ciphers(), setCiphers(), supportedCiphers()
*/
QSslCipher QSslConfiguration::sessionCipher() const
{
@@ -581,15 +550,13 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key)
By default, the handshake phase can choose any of the ciphers
supported by this system's SSL libraries, which may vary from
system to system. The list of ciphers supported by this system's
- SSL libraries is returned by QSslSocket::supportedCiphers(). You can restrict
+ SSL libraries is returned by supportedCiphers(). You can restrict
the list of ciphers used for choosing the session cipher for this
socket by calling setCiphers() with a subset of the supported
ciphers. You can revert to using the entire set by calling
- setCiphers() with the list returned by QSslSocket::supportedCiphers().
-
- \note This is not currently supported in the Schannel backend.
+ setCiphers() with the list returned by supportedCiphers().
- \sa setCiphers(), QSslSocket::supportedCiphers()
+ \sa setCiphers(), supportedCiphers()
*/
QList<QSslCipher> QSslConfiguration::ciphers() const
{
@@ -604,9 +571,7 @@ QList<QSslCipher> QSslConfiguration::ciphers() const
Restricting the cipher suite must be done before the handshake
phase, where the session cipher is chosen.
- \note This is not currently supported in the Schannel backend.
-
- \sa ciphers(), QSslSocket::supportedCiphers()
+ \sa ciphers(), supportedCiphers()
*/
void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
{
@@ -614,6 +579,34 @@ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
}
/*!
+ \since 6.0
+
+ Sets the cryptographic cipher suite for this configuration to \a ciphers,
+ which is a colon-separated list of cipher suite names. The ciphers are listed
+ in order of preference, starting with the most preferred cipher.
+ Each cipher name in \a ciphers must be the name of a cipher in the
+ list returned by supportedCiphers(). Restricting the cipher suite
+ must be done before the handshake phase, where the session cipher
+ is chosen.
+
+ \note With the Schannel backend the order of the ciphers is ignored and Schannel
+ picks the most secure one during the handshake.
+
+ \sa ciphers()
+*/
+void QSslConfiguration::setCiphers(const QString &ciphers)
+{
+ auto *p = d.data();
+ p->ciphers.clear();
+ const auto cipherNames = ciphers.split(u':', Qt::SkipEmptyParts);
+ for (const QString &cipherName : cipherNames) {
+ QSslCipher cipher(cipherName);
+ if (!cipher.isNull())
+ p->ciphers << cipher;
+ }
+}
+
+/*!
\since 5.5
Returns the list of cryptographic ciphers supported by this
@@ -660,6 +653,8 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
}
/*!
+ \since 5.15
+
Searches all files in the \a path for certificates encoded in the
specified \a format and adds them to this socket's CA certificate
database. \a path must be a file or a pattern matching one or more
@@ -675,7 +670,7 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
\sa addCaCertificate(), QSslCertificate::fromPath()
*/
bool QSslConfiguration::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
+ QSslCertificate::PatternSyntax syntax)
{
QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
if (certs.isEmpty())
@@ -780,7 +775,7 @@ bool QSslConfiguration::testSslOption(QSsl::SslOption option) const
knowledge of the session allows for eavesdropping on data
encrypted with the session parameters.
- \sa setSessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa setSessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
QByteArray QSslConfiguration::sessionTicket() const
{
@@ -795,7 +790,7 @@ QByteArray QSslConfiguration::sessionTicket() const
for this to work, and \a sessionTicket must be in ASN.1 format
as returned by sessionTicket().
- \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
void QSslConfiguration::setSessionTicket(const QByteArray &sessionTicket)
{
@@ -813,7 +808,7 @@ void QSslConfiguration::setSessionTicket(const QByteArray &sessionTicket)
QSsl::SslOptionDisableSessionPersistence was not turned off,
this function returns -1.
- \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption()
+ \sa sessionTicket(), QSsl::SslOptionDisableSessionPersistence, setSslOption(), QSslSocket::newSessionTicketReceived()
*/
int QSslConfiguration::sessionTicketLifeTimeHint() const
{
@@ -859,7 +854,7 @@ QSslKey QSslConfiguration::ephemeralServerKey() const
\sa setEllipticCurves
*/
-QVector<QSslEllipticCurve> QSslConfiguration::ellipticCurves() const
+QList<QSslEllipticCurve> QSslConfiguration::ellipticCurves() const
{
return d->ellipticCurves;
}
@@ -876,7 +871,7 @@ QVector<QSslEllipticCurve> QSslConfiguration::ellipticCurves() const
\sa ellipticCurves
*/
-void QSslConfiguration::setEllipticCurves(const QVector<QSslEllipticCurve> &curves)
+void QSslConfiguration::setEllipticCurves(const QList<QSslEllipticCurve> &curves)
{
d->ellipticCurves = curves;
}
@@ -890,7 +885,7 @@ void QSslConfiguration::setEllipticCurves(const QVector<QSslEllipticCurve> &curv
\sa ellipticCurves(), setEllipticCurves()
*/
-QVector<QSslEllipticCurve> QSslConfiguration::supportedEllipticCurves()
+QList<QSslEllipticCurve> QSslConfiguration::supportedEllipticCurves()
{
return QSslSocketPrivate::supportedEllipticCurves();
}
@@ -927,7 +922,11 @@ void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint)
Retrieves the current set of Diffie-Hellman parameters.
If no Diffie-Hellman parameters have been set, the QSslConfiguration object
- defaults to using the 1024-bit MODP group from RFC 2409.
+ defaults to using the 2048-bit MODP group from RFC 3526.
+
+ \note The default parameters may change in future Qt versions.
+ Please check the documentation of the \e{exact Qt version} that you
+ are using in order to know what defaults that version uses.
*/
QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const
{
@@ -941,7 +940,14 @@ QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const
a server to \a dhparams.
If no Diffie-Hellman parameters have been set, the QSslConfiguration object
- defaults to using the 1024-bit MODP group from RFC 2409.
+ defaults to using the 2048-bit MODP group from RFC 3526.
+
+ Since 6.7 you can provide an empty Diffie-Hellman parameter to use auto selection
+ (see SSL_CTX_set_dh_auto of openssl) if the tls backend supports it.
+
+ \note The default parameters may change in future Qt versions.
+ Please check the documentation of the \e{exact Qt version} that you
+ are using in order to know what defaults that version uses.
*/
void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams)
{
@@ -1033,13 +1039,9 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const
Whether or not the negotiation succeeded can be queried through
nextProtocolNegotiationStatus().
- \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1
*/
-#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
void QSslConfiguration::setAllowedNextProtocols(const QList<QByteArray> &protocols)
-#else
-void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
-#endif
{
d->nextAllowedProtocols = protocols;
}
@@ -1051,7 +1053,7 @@ void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
server through the Next Protocol Negotiation (NPN) or Application-Layer
Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols().
- \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
+ \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1
*/
QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
{
@@ -1088,7 +1090,7 @@ QSslConfiguration::NextProtocolNegotiationStatus QSslConfiguration::nextProtocol
supported SSL ciphers that are 128 bits or more
\endlist
- \sa QSslSocket::supportedCiphers(), setDefaultConfiguration()
+ \sa supportedCiphers(), setDefaultConfiguration()
*/
QSslConfiguration QSslConfiguration::defaultConfiguration()
{
@@ -1100,14 +1102,14 @@ QSslConfiguration QSslConfiguration::defaultConfiguration()
connections to be \a configuration. Existing connections are not
affected by this call.
- \sa QSslSocket::supportedCiphers(), defaultConfiguration()
+ \sa supportedCiphers(), defaultConfiguration()
*/
void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configuration)
{
QSslConfigurationPrivate::setDefaultConfiguration(configuration);
}
-#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(dtls) || defined(Q_QDOC)
/*!
This function returns true if DTLS cookie verification was enabled on a
@@ -1197,6 +1199,91 @@ bool QSslConfiguration::ocspStaplingEnabled() const
return d->ocspStaplingEnabled;
}
+/*!
+ \since 6.0
+
+ Returns true if a verification callback will emit QSslSocket::handshakeInterruptedOnError()
+ early, before concluding the handshake.
+
+ \note This function always returns false for all backends but OpenSSL.
+
+ \sa setHandshakeMustInterruptOnError(), QSslSocket::handshakeInterruptedOnError(), QSslSocket::continueInterruptedHandshake()
+*/
+bool QSslConfiguration::handshakeMustInterruptOnError() const
+{
+ return d->reportFromCallback;
+}
+
+/*!
+ \since 6.0
+
+ If \a interrupt is true and the underlying backend supports this option,
+ errors found during certificate verification are reported immediately
+ by emitting QSslSocket::handshakeInterruptedOnError(). This allows
+ to stop the unfinished handshake and send a proper alert message to
+ a peer. No special action is required from the application in this case.
+ QSslSocket will close the connection after sending the alert message.
+ If the application after inspecting the error wants to continue the
+ handshake, it must call QSslSocket::continueInterruptedHandshake()
+ from its slot function. The signal-slot connection must be direct.
+
+ \note When interrupting handshake is enabled, errors that would otherwise
+ be reported by QSslSocket::peerVerifyError() are instead only reported by
+ QSslSocket::handshakeInterruptedOnError().
+ \note Even if the handshake was continued, these errors will be
+ reported when emitting QSslSocket::sslErrors() signal (and thus must
+ be ignored in the corresponding function slot).
+
+ \sa handshakeMustInterruptOnError(), QSslSocket::handshakeInterruptedOnError(), QSslSocket::continueInterruptedHandshake()
+*/
+void QSslConfiguration::setHandshakeMustInterruptOnError(bool interrupt)
+{
+#if QT_CONFIG(openssl)
+ d->reportFromCallback = interrupt;
+#else
+ Q_UNUSED(interrupt);
+ qCWarning(lcSsl, "This operation requires OpenSSL as TLS backend");
+#endif
+}
+
+/*!
+ \since 6.0
+
+ Returns true if errors with code QSslError::NoPeerCertificate
+ cannot be ignored.
+
+ \note Always returns false for all TLS backends but OpenSSL.
+
+ \sa QSslSocket::ignoreSslErrors(), setMissingCertificateIsFatal()
+*/
+bool QSslConfiguration::missingCertificateIsFatal() const
+{
+ return d->missingCertIsFatal;
+}
+
+/*!
+ \since 6.0
+
+ If \a cannotRecover is true, and verification mode in use is
+ QSslSocket::VerifyPeer or QSslSocket::AutoVerifyPeer (for a
+ client-side socket), the missing peer's certificate would be
+ treated as an unrecoverable error that cannot be ignored. A proper
+ alert message will be sent to the peer before closing the connection.
+
+ \note Only available if Qt was configured and built with OpenSSL backend.
+
+ \sa QSslSocket::ignoreSslErrors(), QSslSocket::PeerVerifyMode, missingCertificateIsFatal()
+*/
+void QSslConfiguration::setMissingCertificateIsFatal(bool cannotRecover)
+{
+#if QT_CONFIG(openssl)
+ d->missingCertIsFatal = cannotRecover;
+#else
+ Q_UNUSED(cannotRecover);
+ qCWarning(lcSsl, "Handling a missing certificate as a fatal error requires an OpenSSL backend");
+#endif // openssl
+}
+
/*! \internal
*/
bool QSslConfigurationPrivate::peerSessionWasShared(const QSslConfiguration &configuration) {
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 247f3aecc9..dd2dd2a97c 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/****************************************************************************
**
@@ -66,18 +30,12 @@
QT_BEGIN_NAMESPACE
-template<typename T> class QList;
class QSslCertificate;
class QSslCipher;
class QSslKey;
class QSslEllipticCurve;
class QSslDiffieHellmanParameters;
-namespace dtlsopenssl
-{
-class DtlsState;
-}
-
class QSslConfigurationPrivate;
class Q_NETWORK_EXPORT QSslConfiguration
{
@@ -89,7 +47,7 @@ public:
QSslConfiguration &operator=(const QSslConfiguration &other);
void swap(QSslConfiguration &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
bool operator==(const QSslConfiguration &other) const;
inline bool operator!=(const QSslConfiguration &other) const
@@ -126,13 +84,15 @@ public:
// Cipher settings
QList<QSslCipher> ciphers() const;
void setCiphers(const QList<QSslCipher> &ciphers);
+ void setCiphers(const QString &ciphers);
static QList<QSslCipher> supportedCiphers();
// Certificate Authority (CA) settings
QList<QSslCertificate> caCertificates() const;
void setCaCertificates(const QList<QSslCertificate> &certificates);
- bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ bool addCaCertificates(
+ const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QSslCertificate::PatternSyntax syntax = QSslCertificate::PatternSyntax::FixedString);
void addCaCertificate(const QSslCertificate &certificate);
void addCaCertificates(const QList<QSslCertificate> &certificates);
@@ -148,9 +108,9 @@ public:
QSslKey ephemeralServerKey() const;
// EC settings
- QVector<QSslEllipticCurve> ellipticCurves() const;
- void setEllipticCurves(const QVector<QSslEllipticCurve> &curves);
- static QVector<QSslEllipticCurve> supportedEllipticCurves();
+ QList<QSslEllipticCurve> ellipticCurves() const;
+ void setEllipticCurves(const QList<QSslEllipticCurve> &curves);
+ static QList<QSslEllipticCurve> supportedEllipticCurves();
QByteArray preSharedKeyIdentityHint() const;
void setPreSharedKeyIdentityHint(const QByteArray &hint);
@@ -165,7 +125,7 @@ public:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
-#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(dtls) || defined(Q_QDOC)
bool dtlsCookieVerificationEnabled() const;
void setDtlsCookieVerificationEnabled(bool enable);
@@ -173,6 +133,12 @@ public:
static void setDefaultDtlsConfiguration(const QSslConfiguration &configuration);
#endif // dtls
+ bool handshakeMustInterruptOnError() const;
+ void setHandshakeMustInterruptOnError(bool interrupt);
+
+ bool missingCertificateIsFatal() const;
+ void setMissingCertificateIsFatal(bool cannotRecover);
+
void setOcspStaplingEnabled(bool enable);
bool ocspStaplingEnabled() const;
@@ -182,27 +148,20 @@ public:
NextProtocolNegotiationUnsupported
};
-#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
void setAllowedNextProtocols(const QList<QByteArray> &protocols);
-#else
- void setAllowedNextProtocols(QList<QByteArray> protocols);
-#endif
QList<QByteArray> allowedNextProtocols() const;
QByteArray nextNegotiatedProtocol() const;
NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
static const char ALPNProtocolHTTP2[];
- static const char NextProtocolSpdy3_0[];
static const char NextProtocolHttp1_1[];
private:
friend class QSslSocket;
friend class QSslConfigurationPrivate;
- friend class QSslSocketBackendPrivate;
friend class QSslContext;
- friend class QDtlsBasePrivate;
- friend class dtlsopenssl::DtlsState;
+ friend class QTlsBackend;
QSslConfiguration(QSslConfigurationPrivate *dd);
QSharedDataPointer<QSslConfigurationPrivate> d;
};
@@ -211,7 +170,7 @@ Q_DECLARE_SHARED(QSslConfiguration)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSslConfiguration)
+QT_DECL_METATYPE_EXTERN(QSslConfiguration, Q_NETWORK_EXPORT)
#endif // QT_NO_SSL
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 83126bb9a0..a31e7e1f04 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/****************************************************************************
**
@@ -118,9 +82,9 @@ public:
QSsl::SslOptions sslOptions;
- Q_AUTOTEST_EXPORT static const QSsl::SslOptions defaultSslOptions;
+ static const QSsl::SslOptions defaultSslOptions;
- QVector<QSslEllipticCurve> ellipticCurves;
+ QList<QSslEllipticCurve> ellipticCurves;
QSslDiffieHellmanParameters dhParams;
@@ -149,6 +113,14 @@ public:
const bool ocspStaplingEnabled = false;
#endif
+#if QT_CONFIG(openssl)
+ bool reportFromCallback = false;
+ bool missingCertIsFatal = false;
+#else
+ const bool reportFromCallback = false;
+ const bool missingCertIsFatal = false;
+#endif // openssl
+
// in qsslsocket.cpp:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
deleted file mode 100644
index 562aa4f518..0000000000
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ /dev/null
@@ -1,736 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtNetwork/qsslsocket.h>
-#include <QtNetwork/qssldiffiehellmanparameters.h>
-
-#include "private/qssl_p.h"
-#include "private/qsslsocket_p.h"
-#include "private/qsslcontext_openssl_p.h"
-#include "private/qsslsocket_openssl_p.h"
-#include "private/qsslsocket_openssl_symbols_p.h"
-#include "private/qssldiffiehellmanparameters_p.h"
-
-#include <vector>
-
-QT_BEGIN_NAMESPACE
-
-// defined in qsslsocket_openssl.cpp:
-extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
-extern QString getErrorsFromOpenSsl();
-
-#if QT_CONFIG(dtls)
-// defined in qdtls_openssl.cpp:
-namespace dtlscallbacks
-{
-extern "C" int q_X509DtlsCallback(int ok, X509_STORE_CTX *ctx);
-extern "C" int q_generate_cookie_callback(SSL *ssl, unsigned char *dst,
- unsigned *cookieLength);
-extern "C" int q_verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
- unsigned cookieLength);
-}
-#endif // dtls
-
-// Defined in qsslsocket.cpp
-QList<QSslCipher> q_getDefaultDtlsCiphers();
-
-static inline QString msgErrorSettingBackendConfig(const QString &why)
-{
- return QSslSocket::tr("Error when setting the OpenSSL configuration (%1)").arg(why);
-}
-
-static inline QString msgErrorSettingEllipticCurves(const QString &why)
-{
- return QSslSocket::tr("Error when setting the elliptic curves (%1)").arg(why);
-}
-
-QSslContext::QSslContext()
- : ctx(nullptr),
- pkey(nullptr),
- session(nullptr),
- m_sessionTicketLifeTimeHint(-1)
-{
-}
-
-QSslContext::~QSslContext()
-{
- if (ctx)
- // This will decrement the reference count by 1 and free the context eventually when possible
- q_SSL_CTX_free(ctx);
-
- if (pkey)
- q_EVP_PKEY_free(pkey);
-
- if (session)
- q_SSL_SESSION_free(session);
-}
-
-QSslContext* QSslContext::fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
-{
- QSslContext *sslContext = new QSslContext();
- initSslContext(sslContext, mode, configuration, allowRootCertOnDemandLoading);
- return sslContext;
-}
-
-QSharedPointer<QSslContext> QSslContext::sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration, bool allowRootCertOnDemandLoading)
-{
- QSharedPointer<QSslContext> sslContext = QSharedPointer<QSslContext>::create();
- initSslContext(sslContext.data(), mode, configuration, allowRootCertOnDemandLoading);
- return sslContext;
-}
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-
-static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
- const unsigned char *in, unsigned int inlen, void *arg)
-{
- QSslContext::NPNContext *ctx = reinterpret_cast<QSslContext::NPNContext *>(arg);
-
- // comment out to debug:
-// QList<QByteArray> supportedVersions;
-// for (unsigned int i = 0; i < inlen; ) {
-// QByteArray version(reinterpret_cast<const char *>(&in[i+1]), in[i]);
-// supportedVersions << version;
-// i += in[i] + 1;
-// }
-
- int proto = q_SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
- switch (proto) {
- case OPENSSL_NPN_UNSUPPORTED:
- ctx->status = QSslConfiguration::NextProtocolNegotiationNone;
- break;
- case OPENSSL_NPN_NEGOTIATED:
- ctx->status = QSslConfiguration::NextProtocolNegotiationNegotiated;
- break;
- case OPENSSL_NPN_NO_OVERLAP:
- ctx->status = QSslConfiguration::NextProtocolNegotiationUnsupported;
- break;
- default:
- qCWarning(lcSsl, "OpenSSL sent unknown NPN status");
- }
-
- return SSL_TLSEXT_ERR_OK;
-}
-
-QSslContext::NPNContext QSslContext::npnContext() const
-{
- return m_npnContext;
-}
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
-
-
-// Needs to be deleted by caller
-SSL* QSslContext::createSsl()
-{
- SSL* ssl = q_SSL_new(ctx);
- q_SSL_clear(ssl);
-
- if (!session && !sessionASN1().isEmpty()
- && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
- const unsigned char *data = reinterpret_cast<const unsigned char *>(m_sessionASN1.constData());
- session = q_d2i_SSL_SESSION(
- nullptr, &data, m_sessionASN1.size()); // refcount is 1 already, set by function above
- }
-
- if (session) {
- // Try to resume the last session we cached
- if (!q_SSL_set_session(ssl, session)) {
- qCWarning(lcSsl, "could not set SSL session");
- q_SSL_SESSION_free(session);
- session = nullptr;
- }
- }
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
- QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols;
- if (!protocols.isEmpty()) {
- m_supportedNPNVersions.clear();
- for (int a = 0; a < protocols.count(); ++a) {
- if (protocols.at(a).size() > 255) {
- qCWarning(lcSsl) << "TLS NPN extension" << protocols.at(a)
- << "is too long and will be ignored.";
- continue;
- } else if (protocols.at(a).isEmpty()) {
- continue;
- }
- m_supportedNPNVersions.append(protocols.at(a).size()).append(protocols.at(a));
- }
- if (m_supportedNPNVersions.size()) {
- m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
- m_npnContext.len = m_supportedNPNVersions.count();
- m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
- // Callback's type has a parameter 'const unsigned char ** out'
- // since it was introduced in 1.0.2. Internally, OpenSSL's own code
- // (tests/examples) cast it to unsigned char * (since it's 'out').
- // We just re-use our NPN callback and cast here:
- typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *,
- const unsigned char *, unsigned int, void *);
- // With ALPN callback is for a server side only, for a client m_npnContext.status
- // will stay in NextProtocolNegotiationNone.
- q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext);
- // Client:
- q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len);
- // And in case our peer does not support ALPN, but supports NPN:
- q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
- }
- }
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
- return ssl;
-}
-
-// We cache exactly one session here
-bool QSslContext::cacheSession(SSL* ssl)
-{
- // don't cache the same session again
- if (session && session == q_SSL_get_session(ssl))
- return true;
-
- // decrease refcount of currently stored session
- // (this might happen if there are several concurrent handshakes in flight)
- if (session)
- q_SSL_SESSION_free(session);
-
- // cache the session the caller gave us and increase reference count
- session = q_SSL_get1_session(ssl);
-
- if (session && !sslConfiguration.testSslOption(QSsl::SslOptionDisableSessionPersistence)) {
- int sessionSize = q_i2d_SSL_SESSION(session, nullptr);
- if (sessionSize > 0) {
- m_sessionASN1.resize(sessionSize);
- unsigned char *data = reinterpret_cast<unsigned char *>(m_sessionASN1.data());
- if (!q_i2d_SSL_SESSION(session, &data))
- qCWarning(lcSsl, "could not store persistent version of SSL session");
- m_sessionTicketLifeTimeHint = q_SSL_SESSION_get_ticket_lifetime_hint(session);
- }
- }
-
- return (session != nullptr);
-}
-
-QByteArray QSslContext::sessionASN1() const
-{
- return m_sessionASN1;
-}
-
-void QSslContext::setSessionASN1(const QByteArray &session)
-{
- m_sessionASN1 = session;
-}
-
-int QSslContext::sessionTicketLifeTimeHint() const
-{
- return m_sessionTicketLifeTimeHint;
-}
-
-QSslError::SslError QSslContext::error() const
-{
- return errorCode;
-}
-
-QString QSslContext::errorString() const
-{
- return errorStr;
-}
-
-void QSslContext::initSslContext(QSslContext *sslContext, QSslSocket::SslMode mode,
- const QSslConfiguration &configuration,
- bool allowRootCertOnDemandLoading)
-{
- sslContext->sslConfiguration = configuration;
- sslContext->errorCode = QSslError::NoError;
-
- bool client = (mode == QSslSocket::SslClientMode);
-
- bool reinitialized = false;
- bool unsupportedProtocol = false;
- bool isDtls = false;
-init_context:
- if (sslContext->sslConfiguration.protocol() == QSsl::SslV2) {
- // SSL 2 is no longer supported, but chosen deliberately -> error
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- } else if (sslContext->sslConfiguration.protocol() == QSsl::SslV3) {
- // SSL 3 is no longer supported, but chosen deliberately -> error
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- } else {
- switch (sslContext->sslConfiguration.protocol()) {
- case QSsl::DtlsV1_0:
- case QSsl::DtlsV1_0OrLater:
- case QSsl::DtlsV1_2:
- case QSsl::DtlsV1_2OrLater:
-#if QT_CONFIG(dtls)
- isDtls = true;
- sslContext->ctx = q_SSL_CTX_new(client ? q_DTLS_client_method() : q_DTLS_server_method());
-#else // dtls
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- qCWarning(lcSsl, "DTLS protocol requested, but feature 'dtls' is disabled");
-
-#endif // dtls
- break;
- case QSsl::TlsV1_3:
- case QSsl::TlsV1_3OrLater:
-#if !defined(TLS1_3_VERSION)
- qCWarning(lcSsl, "TLS 1.3 is not supported");
- sslContext->ctx = nullptr;
- unsupportedProtocol = true;
- break;
-#endif // TLS1_3_VERSION
- default:
- // The ssl options will actually control the supported methods
- sslContext->ctx = q_SSL_CTX_new(client ? q_TLS_client_method() : q_TLS_server_method());
- }
- }
-
- if (!sslContext->ctx) {
- // After stopping Flash 10 the SSL library loses its ciphers. Try re-adding them
- // by re-initializing the library.
- if (!reinitialized) {
- reinitialized = true;
- if (q_OPENSSL_init_ssl(0, nullptr) == 1)
- goto init_context;
- }
-
- sslContext->errorStr = QSslSocket::tr("Error creating SSL context (%1)").arg(
- unsupportedProtocol ? QSslSocket::tr("unsupported protocol") : QSslSocketBackendPrivate::getErrorsFromOpenSsl()
- );
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- const long anyVersion =
-#if QT_CONFIG(dtls)
- isDtls ? DTLS_ANY_VERSION : TLS_ANY_VERSION;
-#else
- TLS_ANY_VERSION;
-#endif // dtls
- long minVersion = anyVersion;
- long maxVersion = anyVersion;
-
- switch (sslContext->sslConfiguration.protocol()) {
- case QSsl::TlsV1_0:
- minVersion = TLS1_VERSION;
- maxVersion = TLS1_VERSION;
- break;
- case QSsl::TlsV1_1:
- minVersion = TLS1_1_VERSION;
- maxVersion = TLS1_1_VERSION;
- break;
- case QSsl::TlsV1_2:
- minVersion = TLS1_2_VERSION;
- maxVersion = TLS1_2_VERSION;
- break;
- case QSsl::TlsV1_3:
-#ifdef TLS1_3_VERSION
- minVersion = TLS1_3_VERSION;
- maxVersion = TLS1_3_VERSION;
-#else
- // This protocol is not supported by OpenSSL 1.1 and we handle
- // it as an error (see the code above).
- Q_UNREACHABLE();
-#endif // TLS1_3_VERSION
- break;
- // Ranges:
- case QSsl::TlsV1SslV3:
- case QSsl::AnyProtocol:
- case QSsl::SecureProtocols:
- case QSsl::TlsV1_0OrLater:
- minVersion = TLS1_VERSION;
- maxVersion = 0;
- break;
- case QSsl::TlsV1_1OrLater:
- minVersion = TLS1_1_VERSION;
- maxVersion = 0;
- break;
- case QSsl::TlsV1_2OrLater:
- minVersion = TLS1_2_VERSION;
- maxVersion = 0;
- break;
- case QSsl::DtlsV1_0:
- minVersion = DTLS1_VERSION;
- maxVersion = DTLS1_VERSION;
- break;
- case QSsl::DtlsV1_0OrLater:
- minVersion = DTLS1_VERSION;
- maxVersion = DTLS_MAX_VERSION;
- break;
- case QSsl::DtlsV1_2:
- minVersion = DTLS1_2_VERSION;
- maxVersion = DTLS1_2_VERSION;
- break;
- case QSsl::DtlsV1_2OrLater:
- minVersion = DTLS1_2_VERSION;
- maxVersion = DTLS_MAX_VERSION;
- break;
- case QSsl::TlsV1_3OrLater:
-#ifdef TLS1_3_VERSION
- minVersion = TLS1_3_VERSION;
- maxVersion = 0;
- break;
-#else
- // This protocol is not supported by OpenSSL 1.1 and we handle
- // it as an error (see the code above).
- Q_UNREACHABLE();
- break;
-#endif // TLS1_3_VERSION
- case QSsl::SslV2:
- case QSsl::SslV3:
- // These protocols are not supported, and we handle
- // them as an error (see the code above).
- Q_UNREACHABLE();
- break;
- case QSsl::UnknownProtocol:
- break;
- }
-
- if (minVersion != anyVersion
- && !q_SSL_CTX_set_min_proto_version(sslContext->ctx, minVersion)) {
- sslContext->errorStr = QSslSocket::tr("Error while setting the minimal protocol version");
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- if (maxVersion != anyVersion
- && !q_SSL_CTX_set_max_proto_version(sslContext->ctx, maxVersion)) {
- sslContext->errorStr = QSslSocket::tr("Error while setting the maximum protocol version");
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- // Enable bug workarounds.
- long options = QSslSocketBackendPrivate::setupOpenSslOptions(configuration.protocol(), configuration.d->sslOptions);
- q_SSL_CTX_set_options(sslContext->ctx, options);
-
- // Tell OpenSSL to release memory early
- // http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html
- q_SSL_CTX_set_mode(sslContext->ctx, SSL_MODE_RELEASE_BUFFERS);
-
- auto filterCiphers = [](const QList<QSslCipher> &ciphers, bool selectTls13)
- {
- QByteArray cipherString;
- bool first = true;
-
- for (const QSslCipher &cipher : qAsConst(ciphers)) {
- const bool isTls13Cipher = cipher.protocol() == QSsl::TlsV1_3 || cipher.protocol() == QSsl::TlsV1_3OrLater;
- if (selectTls13 != isTls13Cipher)
- continue;
-
- if (first)
- first = false;
- else
- cipherString.append(':');
- cipherString.append(cipher.name().toLatin1());
- }
- return cipherString;
- };
-
- // Initialize ciphers
- QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
- if (ciphers.isEmpty())
- ciphers = isDtls ? q_getDefaultDtlsCiphers() : QSslSocketPrivate::defaultCiphers();
-
- const QByteArray preTls13Ciphers = filterCiphers(ciphers, false);
-
- if (preTls13Ciphers.size()) {
- if (!q_SSL_CTX_set_cipher_list(sslContext->ctx, preTls13Ciphers.data())) {
- sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
- }
-
- const QByteArray tls13Ciphers = filterCiphers(ciphers, true);
-#ifdef TLS1_3_VERSION
- if (tls13Ciphers.size()) {
- if (!q_SSL_CTX_set_ciphersuites(sslContext->ctx, tls13Ciphers.data())) {
- sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
- }
-#endif // TLS1_3_VERSION
- if (!preTls13Ciphers.size() && !tls13Ciphers.size()) {
- sslContext->errorStr = QSslSocket::tr("Invalid or empty cipher list (%1)").arg(QStringLiteral(""));
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- const QDateTime now = QDateTime::currentDateTimeUtc();
-
- // Add all our CAs to this store.
- const auto caCertificates = sslContext->sslConfiguration.caCertificates();
- for (const QSslCertificate &caCertificate : caCertificates) {
- // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
- //
- // If several CA certificates matching the name, key identifier, and
- // serial number condition are available, only the first one will be
- // examined. This may lead to unexpected results if the same CA
- // certificate is available with different expiration dates. If a
- // ``certificate expired'' verification error occurs, no other
- // certificate will be searched. Make sure to not have expired
- // certificates mixed with valid ones.
- //
- // See also: QSslSocketBackendPrivate::verify()
- if (caCertificate.expiryDate() >= now) {
- q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle());
- }
- }
-
- if (QSslSocketPrivate::s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) {
- // tell OpenSSL the directories where to look up the root certs on demand
- const QList<QByteArray> unixDirs = QSslSocketPrivate::unixRootCertDirectories();
- for (const QByteArray &unixDir : unixDirs)
- q_SSL_CTX_load_verify_locations(sslContext->ctx, nullptr, unixDir.constData());
- }
-
- if (!sslContext->sslConfiguration.localCertificate().isNull()) {
- // Require a private key as well.
- if (sslContext->sslConfiguration.privateKey().isNull()) {
- sslContext->errorStr = QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- // Load certificate
- if (!q_SSL_CTX_use_certificate(sslContext->ctx, (X509 *)sslContext->sslConfiguration.localCertificate().handle())) {
- sslContext->errorStr = QSslSocket::tr("Error loading local certificate, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- if (configuration.d->privateKey.algorithm() == QSsl::Opaque) {
- sslContext->pkey = reinterpret_cast<EVP_PKEY *>(configuration.d->privateKey.handle());
- } else {
- // Load private key
- sslContext->pkey = q_EVP_PKEY_new();
- // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
- // this lead to a memory leak. Now we use the *_set1_* functions which do not
- // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
- if (configuration.d->privateKey.algorithm() == QSsl::Rsa)
- q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
- else if (configuration.d->privateKey.algorithm() == QSsl::Dsa)
- q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle()));
-#ifndef OPENSSL_NO_EC
- else if (configuration.d->privateKey.algorithm() == QSsl::Ec)
- q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle()));
-#endif
- }
-
- if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, sslContext->pkey)) {
- sslContext->errorStr = QSslSocket::tr("Error loading private key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
- if (configuration.d->privateKey.algorithm() == QSsl::Opaque)
- sslContext->pkey = nullptr; // Don't free the private key, it belongs to QSslKey
-
- // Check if the certificate matches the private key.
- if (!q_SSL_CTX_check_private_key(sslContext->ctx)) {
- sslContext->errorStr = QSslSocket::tr("Private key does not certify public key, %1").arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- // If we have any intermediate certificates then we need to add them to our chain
- bool first = true;
- for (const QSslCertificate &cert : qAsConst(configuration.d->localCertificateChain)) {
- if (first) {
- first = false;
- continue;
- }
- q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0,
- q_X509_dup(reinterpret_cast<X509 *>(cert.handle())));
- }
- }
-
- // Initialize peer verification.
- if (sslContext->sslConfiguration.peerVerifyMode() == QSslSocket::VerifyNone) {
- q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_NONE, nullptr);
- } else {
- q_SSL_CTX_set_verify(sslContext->ctx, SSL_VERIFY_PEER,
-#if QT_CONFIG(dtls)
- isDtls ? dtlscallbacks::q_X509DtlsCallback :
-#endif // dtls
- q_X509Callback);
- }
-
-#if QT_CONFIG(dtls)
- if (mode == QSslSocket::SslServerMode && isDtls && configuration.dtlsCookieVerificationEnabled()) {
- q_SSL_CTX_set_cookie_generate_cb(sslContext->ctx, dtlscallbacks::q_generate_cookie_callback);
- q_SSL_CTX_set_cookie_verify_cb(sslContext->ctx, dtlscallbacks::q_verify_cookie_callback);
- }
-#endif // dtls
-
- // Set verification depth.
- if (sslContext->sslConfiguration.peerVerifyDepth() != 0)
- q_SSL_CTX_set_verify_depth(sslContext->ctx, sslContext->sslConfiguration.peerVerifyDepth());
-
- // set persisted session if the user set it
- if (!configuration.sessionTicket().isEmpty())
- sslContext->setSessionASN1(configuration.sessionTicket());
-
- // Set temp DH params
- QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters();
-
- if (!dhparams.isValid()) {
- sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid");
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-
- if (!dhparams.isEmpty()) {
- const QByteArray &params = dhparams.d->derData;
- const char *ptr = params.constData();
- DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
- params.length());
- if (dh == nullptr)
- qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
- q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
- q_DH_free(dh);
- }
-
-#ifndef OPENSSL_NO_PSK
- if (!client)
- q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
-#endif // !OPENSSL_NO_PSK
-
- const QVector<QSslEllipticCurve> qcurves = sslContext->sslConfiguration.ellipticCurves();
- if (!qcurves.isEmpty()) {
-#ifdef OPENSSL_NO_EC
- sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocket::tr("OpenSSL version with disabled elliptic curves"));
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
-#else
- // Set the curves to be used.
- std::vector<int> curves;
- curves.reserve(qcurves.size());
- for (const auto &sslCurve : qcurves)
- curves.push_back(sslCurve.id);
- if (!q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_CURVES, long(curves.size()), &curves[0])) {
- sslContext->errorStr = msgErrorSettingEllipticCurves(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
- sslContext->errorCode = QSslError::UnspecifiedError;
- return;
- }
-#endif
- }
-
- applyBackendConfig(sslContext);
-}
-
-#if QT_CONFIG(ocsp)
-extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp.
-#endif // ocsp
-// static
-void QSslContext::applyBackendConfig(QSslContext *sslContext)
-{
- const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
- if (conf.isEmpty())
- return;
-
-#if QT_CONFIG(ocsp)
- auto ocspResponsePos = conf.find("Qt-OCSP-response");
- if (ocspResponsePos != conf.end()) {
- // This is our private, undocumented configuration option, existing only for
- // the purpose of testing OCSP status responses. We don't even check this
- // callback was set. If no - the test must fail.
- q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, qt_OCSP_status_server_callback);
- if (conf.size() == 1)
- return;
- }
-#endif // ocsp
-
- QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
- if (cctx) {
- q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
- q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
-
- for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
- if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
- continue;
-
- if (!i.value().canConvert(QMetaType::QByteArray)) {
- sslContext->errorCode = QSslError::UnspecifiedError;
- sslContext->errorStr = msgErrorSettingBackendConfig(
- QSslSocket::tr("Expecting QByteArray for %1").arg(
- QString::fromUtf8(i.key())));
- return;
- }
-
- const QByteArray &value = i.value().toByteArray();
- const int result = q_SSL_CONF_cmd(cctx.data(), i.key().constData(), value.constData());
- if (result == 2)
- continue;
-
- sslContext->errorCode = QSslError::UnspecifiedError;
- switch (result) {
- case 0:
- sslContext->errorStr = msgErrorSettingBackendConfig(
- QSslSocket::tr("An error occurred attempting to set %1 to %2").arg(
- QString::fromUtf8(i.key()), QString::fromUtf8(value)));
- return;
- case 1:
- sslContext->errorStr = msgErrorSettingBackendConfig(
- QSslSocket::tr("Wrong value for %1 (%2)").arg(
- QString::fromUtf8(i.key()), QString::fromUtf8(value)));
- return;
- default:
- sslContext->errorStr = msgErrorSettingBackendConfig(
- QSslSocket::tr("Unrecognized command %1 = %2").arg(
- QString::fromUtf8(i.key()), QString::fromUtf8(value)));
- return;
- }
- }
-
- if (q_SSL_CONF_CTX_finish(cctx.data()) == 0) {
- sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_finish() failed"));
- sslContext->errorCode = QSslError::UnspecifiedError;
- }
- } else {
- sslContext->errorStr = msgErrorSettingBackendConfig(QSslSocket::tr("SSL_CONF_CTX_new() failed"));
- sslContext->errorCode = QSslError::UnspecifiedError;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h
deleted file mode 100644
index 70cb97aad8..0000000000
--- a/src/network/ssl/qsslcontext_openssl_p.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#ifndef QSSLCONTEXT_OPENSSL_P_H
-#define QSSLCONTEXT_OPENSSL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qvariant.h>
-#include <QtNetwork/qsslcertificate.h>
-#include <QtNetwork/qsslconfiguration.h>
-#include <openssl/ssl.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SSL
-
-class QSslContextPrivate;
-
-class QSslContext
-{
-public:
-
- ~QSslContext();
-
- static QSslContext* fromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration,
- bool allowRootCertOnDemandLoading);
- static QSharedPointer<QSslContext> sharedFromConfiguration(QSslSocket::SslMode mode, const QSslConfiguration &configuration,
- bool allowRootCertOnDemandLoading);
-
- QSslError::SslError error() const;
- QString errorString() const;
-
- SSL* createSsl();
- bool cacheSession(SSL*); // should be called when handshake completed
-
- QByteArray sessionASN1() const;
- void setSessionASN1(const QByteArray &sessionASN1);
- int sessionTicketLifeTimeHint() const;
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
- // must be public because we want to use it from an OpenSSL callback
- struct NPNContext {
- NPNContext() : data(nullptr),
- len(0),
- status(QSslConfiguration::NextProtocolNegotiationNone)
- { }
- unsigned char *data;
- unsigned short len;
- QSslConfiguration::NextProtocolNegotiationStatus status;
- };
- NPNContext npnContext() const;
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
-protected:
- QSslContext();
- friend class QSharedPointer<QSslContext>;
-
-private:
- static void initSslContext(QSslContext* sslContext, QSslSocket::SslMode mode, const QSslConfiguration &configuration,
- bool allowRootCertOnDemandLoading);
- static void applyBackendConfig(QSslContext *sslContext);
-
-private:
- SSL_CTX* ctx;
- EVP_PKEY *pkey;
- SSL_SESSION *session;
- QByteArray m_sessionASN1;
- int m_sessionTicketLifeTimeHint;
- QSslError::SslError errorCode;
- QString errorStr;
- QSslConfiguration sslConfiguration;
-#ifndef OPENSSL_NO_NEXTPROTONEG
- QByteArray m_supportedNPNVersions;
- NPNContext m_npnContext;
-#endif // !OPENSSL_NO_NEXTPROTONEG
-};
-
-#endif // QT_NO_SSL
-
-QT_END_NAMESPACE
-
-#endif // QSSLCONTEXT_OPENSSL_P_H
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
index 7807afaa30..7da14f3536 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -56,6 +20,7 @@
#include "qssldiffiehellmanparameters.h"
#include "qssldiffiehellmanparameters_p.h"
+#include "qtlsbackend_p.h"
#include "qsslsocket.h"
#include "qsslsocket_p.h"
@@ -68,17 +33,18 @@
QT_BEGIN_NAMESPACE
-// The 1024-bit MODP group from RFC 2459 (Second Oakley Group)
+// The 2048-bit MODP group from RFC 3526
Q_AUTOTEST_EXPORT const char *qssl_dhparams_default_base64 =
- "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR"
- "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL"
- "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC";
+ "MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxObIlFKCHmO"
+ "NATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjftawv/XLb0Brft7jhr"
+ "+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXTmmkWP6j9JM9fg2VdI9yjrZYc"
+ "YvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhghfDKQXkYuNs474553LBgOhgObJ4Oi7Aei"
+ "j7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==";
/*!
Returns the default QSslDiffieHellmanParameters used by QSslSocket.
- This is currently the 1024-bit MODP group from RFC 2459, also
- known as the Second Oakley Group.
+ This is currently the 2048-bit MODP group from RFC 3526.
*/
QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters()
{
@@ -117,12 +83,15 @@ QSslDiffieHellmanParameters::QSslDiffieHellmanParameters()
QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat encoding)
{
QSslDiffieHellmanParameters result;
+ const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
+ if (!tlsBackend)
+ return result;
switch (encoding) {
case QSsl::Der:
- result.d->decodeDer(encoded);
+ result.d->initFromDer(encoded);
break;
case QSsl::Pem:
- result.d->decodePem(encoded);
+ result.d->initFromPem(encoded);
break;
}
return result;
@@ -273,19 +242,47 @@ QString QSslDiffieHellmanParameters::errorString() const noexcept
return QCoreApplication::translate("QSslDiffieHellmanParameter", "The given Diffie-Hellman parameters are deemed unsafe");
}
- Q_UNREACHABLE();
- return QString();
+ Q_UNREACHABLE_RETURN(QString());
}
/*!
+ \fn bool QSslDiffieHellmanParameters::operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
\since 5.8
- \relates QSslDiffieHellmanParameters
Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
*/
-bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
+
+/*!
+ \fn bool QSslDiffieHellmanParameters::operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
+ \since 5.8
+
+ Returns \c true if \a lhs is not equal to \a rhs; otherwise returns \c false.
+*/
+
+/*!
+ \internal
+*/
+bool QSslDiffieHellmanParameters::isEqual(const QSslDiffieHellmanParameters &other) const noexcept
+{
+ return d->derData == other.d->derData;
+}
+
+/*!
+ \internal
+*/
+void QSslDiffieHellmanParametersPrivate::initFromDer(const QByteArray &der)
+{
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ error = QSslDiffieHellmanParameters::Error(tlsBackend->dhParametersFromDer(der, &derData));
+}
+
+/*!
+ \internal
+*/
+void QSslDiffieHellmanParametersPrivate::initFromPem(const QByteArray &pem)
{
- return lhs.d->derData == rhs.d->derData;
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ error = QSslDiffieHellmanParameters::Error(tlsBackend->dhParametersFromPem(pem, &derData));
}
#ifndef QT_NO_DEBUG_STREAM
@@ -316,7 +313,7 @@ QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam)
Returns an hash value for \a dhparam, using \a seed to seed
the calculation.
*/
-uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept
+size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed) noexcept
{
return qHash(dhparam.d->derData, seed);
}
diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h
index f62a3b8f44..d1a525ba26 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.h
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLDIFFIEHELLMANPARAMETERS_H
@@ -56,21 +20,14 @@ class QSslDiffieHellmanParametersPrivate;
class QSslDiffieHellmanParameters;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparams);
#endif
-Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept;
-
-inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
-{
- return !operator==(lhs, rhs);
-}
-
-class QSslDiffieHellmanParameters
+class Q_NETWORK_EXPORT QSslDiffieHellmanParameters
{
public:
enum Error {
@@ -79,34 +36,40 @@ public:
UnsafeParametersError
};
- Q_NETWORK_EXPORT static QSslDiffieHellmanParameters defaultParameters();
+ static QSslDiffieHellmanParameters defaultParameters();
- Q_NETWORK_EXPORT QSslDiffieHellmanParameters();
- Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
+ QSslDiffieHellmanParameters();
+ QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) noexcept : d(other.d) { other.d = nullptr; }
- Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters();
+ ~QSslDiffieHellmanParameters();
- Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
+ QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) noexcept { swap(other); return *this; }
- void swap(QSslDiffieHellmanParameters &other) noexcept { qSwap(d, other.d); }
+ void swap(QSslDiffieHellmanParameters &other) noexcept { qt_ptr_swap(d, other.d); }
- Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
- Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+ static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
+ static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
- Q_NETWORK_EXPORT bool isEmpty() const noexcept;
- Q_NETWORK_EXPORT bool isValid() const noexcept;
- Q_NETWORK_EXPORT Error error() const noexcept;
- Q_NETWORK_EXPORT QString errorString() const noexcept;
+ bool isEmpty() const noexcept;
+ bool isValid() const noexcept;
+ Error error() const noexcept;
+ QString errorString() const noexcept;
private:
QSslDiffieHellmanParametersPrivate *d;
friend class QSslContext;
- friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept;
+
+ bool isEqual(const QSslDiffieHellmanParameters &other) const noexcept;
+ friend bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept
+ { return !lhs.isEqual(rhs); }
+
#ifndef QT_NO_DEBUG_STREAM
friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam);
#endif
- friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) noexcept;
+ friend Q_NETWORK_EXPORT size_t qHash(const QSslDiffieHellmanParameters &dhparam, size_t seed) noexcept;
};
Q_DECLARE_SHARED(QSslDiffieHellmanParameters)
diff --git a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp
deleted file mode 100644
index 8fcf141f73..0000000000
--- a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include "qssldiffiehellmanparameters.h"
-#include "qssldiffiehellmanparameters_p.h"
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qbytearray.h>
-
-QT_BEGIN_NAMESPACE
-
-void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &)
-{
-}
-
-void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &)
-{
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
deleted file mode 100644
index c36482329a..0000000000
--- a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qssldiffiehellmanparameters.h"
-#include "qssldiffiehellmanparameters_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslsocket.h"
-#include "qsslsocket_p.h"
-
-#include <QtCore/qatomic.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qiodevice.h>
-#ifndef QT_NO_DEBUG_STREAM
-#include <QtCore/qdebug.h>
-#endif
-
-#include <openssl/bn.h>
-#include <openssl/dh.h>
-
-QT_BEGIN_NAMESPACE
-
-static bool isSafeDH(DH *dh)
-{
- int status = 0;
- int bad = 0;
-
- QSslSocketPrivate::ensureInitialized();
-
-
- // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
- //
- // The additional call to BN_mod_word(dh->p, 24)
- // (and unmasking of DH_NOT_SUITABLE_GENERATOR)
- // is performed to ensure your program accepts
- // IETF group parameters. OpenSSL checks the prime
- // is congruent to 11 when g = 2; while the IETF's
- // primes are congruent to 23 when g = 2.
- // Without the test, the IETF parameters would
- // fail validation. For details, see Diffie-Hellman
- // Parameter Check (when g = 2, must p mod 24 == 11?).
-#if QT_CONFIG(opensslv11)
- // Mark p < 1024 bits as unsafe.
- if (q_DH_bits(dh) < 1024)
- return false;
-
- if (q_DH_check(dh, &status) != 1)
- return false;
-
- const BIGNUM *p = nullptr;
- const BIGNUM *q = nullptr;
- const BIGNUM *g = nullptr;
- q_DH_get0_pqg(dh, &p, &q, &g);
-
- if (q_BN_is_word(const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
- long residue = q_BN_mod_word(p, 24);
- if (residue == 11 || residue == 23)
- status &= ~DH_NOT_SUITABLE_GENERATOR;
- }
-
-#else
- // Mark p < 1024 bits as unsafe.
- if (q_BN_num_bits(dh->p) < 1024)
- return false;
-
- if (q_DH_check(dh, &status) != 1)
- return false;
-
- if (q_BN_is_word(dh->g, DH_GENERATOR_2)) {
- long residue = q_BN_mod_word(dh->p, 24);
- if (residue == 11 || residue == 23)
- status &= ~DH_NOT_SUITABLE_GENERATOR;
- }
-#endif
-
- bad |= DH_CHECK_P_NOT_PRIME;
- bad |= DH_CHECK_P_NOT_SAFE_PRIME;
- bad |= DH_NOT_SUITABLE_GENERATOR;
-
- return !(status & bad);
-}
-
-void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der)
-{
- if (der.isEmpty()) {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- return;
- }
-
- const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
- int len = der.size();
-
- QSslSocketPrivate::ensureInitialized();
-
- DH *dh = q_d2i_DHparams(nullptr, &data, len);
- if (dh) {
- if (isSafeDH(dh))
- derData = der;
- else
- error = QSslDiffieHellmanParameters::UnsafeParametersError;
- } else {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- }
-
- q_DH_free(dh);
-}
-
-void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem)
-{
- if (pem.isEmpty()) {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- return;
- }
-
- if (!QSslSocket::supportsSsl()) {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- return;
- }
-
- QSslSocketPrivate::ensureInitialized();
-
- BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
- if (!bio) {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- return;
- }
-
- DH *dh = nullptr;
- q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr);
-
- if (dh) {
- if (isSafeDH(dh)) {
- char *buf = nullptr;
- int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf));
- if (len > 0)
- derData = QByteArray(buf, len);
- else
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- } else {
- error = QSslDiffieHellmanParameters::UnsafeParametersError;
- }
- } else {
- error = QSslDiffieHellmanParameters::InvalidInputDataError;
- }
-
- q_DH_free(dh);
- q_BIO_free(bio);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qssldiffiehellmanparameters_p.h b/src/network/ssl/qssldiffiehellmanparameters_p.h
index dd69895dae..705e0f007c 100644
--- a/src/network/ssl/qssldiffiehellmanparameters_p.h
+++ b/src/network/ssl/qssldiffiehellmanparameters_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLDIFFIEHELLMANPARAMETERS_P_H
@@ -53,23 +17,20 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QSharedData>
-#include "qsslkey.h"
#include "qssldiffiehellmanparameters.h"
-#include "qsslsocket_p.h" // includes wincrypt.h
+
+#include <QSharedData>
QT_BEGIN_NAMESPACE
class QSslDiffieHellmanParametersPrivate : public QSharedData
{
public:
- QSslDiffieHellmanParametersPrivate() : error(QSslDiffieHellmanParameters::NoError) {};
-
- void decodeDer(const QByteArray &der);
- void decodePem(const QByteArray &pem);
+ void initFromDer(const QByteArray &der);
+ void initFromPem(const QByteArray &pem);
- QSslDiffieHellmanParameters::Error error;
+ QSslDiffieHellmanParameters::Error error = QSslDiffieHellmanParameters::NoError;
QByteArray derData;
};
diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp
index 5608d32fa7..77aa66f3cc 100644
--- a/src/network/ssl/qsslellipticcurve.cpp
+++ b/src/network/ssl/qsslellipticcurve.cpp
@@ -1,43 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsslellipticcurve.h"
+#include "qtlsbackend_p.h"
+#include "qsslsocket_p.h"
#ifndef QT_NO_DEBUG_STREAM
#include <QDebug>
@@ -45,6 +11,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QSslEllipticCurve)
+
/*!
\class QSslEllipticCurve
\since 5.5
@@ -77,8 +45,6 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name)
-
Returns an QSslEllipticCurve instance representing the
named curve \a name. The \a name is the conventional short
name for the curve, as represented by RFC 4492 (for instance \c{secp521r1}),
@@ -91,10 +57,19 @@ QT_BEGIN_NAMESPACE
\sa shortName()
*/
+QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name)
+{
+ QSslEllipticCurve result;
+ if (name.isEmpty())
+ return result;
-/*!
- \fn QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ result.id = tlsBackend->curveIdFromShortName(name);
+ return result;
+}
+
+/*!
Returns an QSslEllipticCurve instance representing the named curve \a name.
The \a name is a long name for the curve, whose exact spelling depends on the
SSL implementation.
@@ -105,24 +80,49 @@ QT_BEGIN_NAMESPACE
\sa longName()
*/
+QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
+{
+ QSslEllipticCurve result;
+ if (name.isEmpty())
+ return result;
-/*!
- \fn QString QSslEllipticCurve::shortName() const
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ result.id = tlsBackend->curveIdFromLongName(name);
+
+ return result;
+}
+/*!
Returns the conventional short name for this curve. If this
curve is invalid, returns an empty string.
\sa longName()
*/
+QString QSslEllipticCurve::shortName() const
+{
+ QString name;
-/*!
- \fn QString QSslEllipticCurve::longName() const
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ name = tlsBackend->shortNameForId(id);
+ return name;
+}
+
+/*!
Returns the conventional long name for this curve. If this
curve is invalid, returns an empty string.
\sa shortName()
*/
+QString QSslEllipticCurve::longName() const
+{
+ QString name;
+
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ name = tlsBackend->longNameForId(id);
+
+ return name;
+}
/*!
\fn bool QSslEllipticCurve::isValid() const
@@ -131,32 +131,36 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QSslEllipticCurve::isTlsNamedCurve() const
-
Returns true if this elliptic curve is one of the named curves that can be
used in the key exchange when using an elliptic curve cipher with TLS;
false otherwise.
*/
+bool QSslEllipticCurve::isTlsNamedCurve() const noexcept
+{
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->isTlsNamedCurve(id);
+
+ return false;
+}
+
/*!
- \fn bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs)
+ \fn bool QSslEllipticCurve::operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs)
\since 5.5
- \relates QSslEllipticCurve
Returns true if the curve \a lhs represents the same curve of \a rhs;
*/
/*!
- \fn bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs)
+ \fn bool QSslEllipticCurve::operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs)
\since 5.5
- \relates QSslEllipticCurve
Returns true if the curve \a lhs represents a different curve than \a rhs;
false otherwise.
*/
/*!
- \fn uint qHash(QSslEllipticCurve curve, uint seed)
+ \fn size_t qHash(QSslEllipticCurve curve, size_t seed = 0)
\since 5.5
\relates QHash
diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h
index 28de3a03b4..0585ffbd0e 100644
--- a/src/network/ssl/qsslellipticcurve.h
+++ b/src/network/ssl/qsslellipticcurve.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLELLIPTICCURVE_H
#define QSSLELLIPTICCURVE_H
@@ -43,20 +7,17 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QString>
#include <QtCore/QMetaType>
-#if QT_DEPRECATED_SINCE(5, 6)
-#include <QtCore/QHash>
-#endif
#include <QtCore/qhashfunctions.h>
QT_BEGIN_NAMESPACE
class QSslEllipticCurve;
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
-Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed = 0) noexcept;
+constexpr size_t qHash(QSslEllipticCurve curve, size_t seed = 0) noexcept;
class QSslEllipticCurve {
public:
- Q_DECL_CONSTEXPR QSslEllipticCurve() noexcept
+ constexpr QSslEllipticCurve() noexcept
: id(0)
{
}
@@ -64,10 +25,10 @@ public:
Q_NETWORK_EXPORT static QSslEllipticCurve fromShortName(const QString &name);
Q_NETWORK_EXPORT static QSslEllipticCurve fromLongName(const QString &name);
- Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString shortName() const;
- Q_REQUIRED_RESULT Q_NETWORK_EXPORT QString longName() const;
+ [[nodiscard]] Q_NETWORK_EXPORT QString shortName() const;
+ [[nodiscard]] Q_NETWORK_EXPORT QString longName() const;
- Q_DECL_CONSTEXPR bool isValid() const noexcept
+ constexpr bool isValid() const noexcept
{
return id != 0;
}
@@ -77,25 +38,21 @@ public:
private:
int id;
- friend Q_DECL_CONSTEXPR bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept;
- friend Q_DECL_CONSTEXPR uint qHash(QSslEllipticCurve curve, uint seed) noexcept;
+ friend constexpr bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
+ { return lhs.id == rhs.id; }
+ friend constexpr bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
+ { return !(lhs == rhs); }
+ friend constexpr size_t qHash(QSslEllipticCurve curve, size_t seed) noexcept;
friend class QSslContext;
friend class QSslSocketPrivate;
- friend class QSslSocketBackendPrivate;
};
Q_DECLARE_TYPEINFO(QSslEllipticCurve, Q_PRIMITIVE_TYPE);
-Q_DECL_CONSTEXPR inline uint qHash(QSslEllipticCurve curve, uint seed) noexcept
+constexpr inline size_t qHash(QSslEllipticCurve curve, size_t seed) noexcept
{ return qHash(curve.id, seed); }
-Q_DECL_CONSTEXPR inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
-{ return lhs.id == rhs.id; }
-
-Q_DECL_CONSTEXPR inline bool operator!=(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
-{ return !operator==(lhs, rhs); }
-
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslEllipticCurve curve);
@@ -103,6 +60,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslEllipticCurve curve);
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSslEllipticCurve)
+QT_DECL_METATYPE_EXTERN(QSslEllipticCurve, Q_NETWORK_EXPORT)
#endif // QSSLELLIPTICCURVE_H
diff --git a/src/network/ssl/qsslellipticcurve_dummy.cpp b/src/network/ssl/qsslellipticcurve_dummy.cpp
deleted file mode 100644
index 1313e06875..0000000000
--- a/src/network/ssl/qsslellipticcurve_dummy.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslellipticcurve.h"
-
-QT_BEGIN_NAMESPACE
-
-QString QSslEllipticCurve::shortName() const
-{
- return QString();
-}
-
-QString QSslEllipticCurve::longName() const
-{
- return QString();
-}
-
-QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name)
-{
- Q_UNUSED(name);
- return QSslEllipticCurve();
-}
-
-QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
-{
- Q_UNUSED(name);
- return QSslEllipticCurve();
-}
-
-bool QSslEllipticCurve::isTlsNamedCurve() const noexcept
-{
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslellipticcurve_openssl.cpp b/src/network/ssl/qsslellipticcurve_openssl.cpp
deleted file mode 100644
index bb7ad66bd2..0000000000
--- a/src/network/ssl/qsslellipticcurve_openssl.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslellipticcurve.h"
-#include "qsslsocket_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-
-#include <openssl/ssl.h>
-#include <openssl/obj_mac.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-QString QSslEllipticCurve::shortName() const
-{
- QString result;
-#ifndef OPENSSL_NO_EC
- if (id != 0)
- result = QString::fromLatin1(q_OBJ_nid2sn(id));
-#endif
- return result;
-}
-
-QString QSslEllipticCurve::longName() const
-{
- QString result;
-#ifndef OPENSSL_NO_EC
- if (id != 0)
- result = QString::fromLatin1(q_OBJ_nid2ln(id));
-#endif
- return result;
-}
-
-QSslEllipticCurve QSslEllipticCurve::fromShortName(const QString &name)
-{
- if (name.isEmpty())
- return QSslEllipticCurve();
-
- QSslSocketPrivate::ensureInitialized();
-
- QSslEllipticCurve result;
-
-#ifndef OPENSSL_NO_EC
-
- const QByteArray curveNameLatin1 = name.toLatin1();
- int nid = q_OBJ_sn2nid(curveNameLatin1.data());
-
- if (nid == 0)
- nid = q_EC_curve_nist2nid(curveNameLatin1.data());
-
- result.id = nid;
-
-#endif // !OPENSSL_NO_EC
-
- return result;
-}
-
-QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
-{
- if (name.isEmpty())
- return QSslEllipticCurve();
-
- QSslSocketPrivate::ensureInitialized();
-
- QSslEllipticCurve result;
-
-#ifndef OPENSSL_NO_EC
- const QByteArray curveNameLatin1 = name.toLatin1();
-
- int nid = q_OBJ_ln2nid(curveNameLatin1.data());
- result.id = nid;
-#endif
-
- return result;
-}
-
-
-// The brainpool curve NIDs (RFC 7027) have been introduced in OpenSSL 1.0.2,
-// redefine them here to make Qt compile with previous versions of OpenSSL
-// (yet correctly recognize them as TLS named curves).
-// See crypto/objects/obj_mac.h
-#ifndef NID_brainpoolP256r1
-#define NID_brainpoolP256r1 927
-#endif
-
-#ifndef NID_brainpoolP384r1
-#define NID_brainpoolP384r1 931
-#endif
-
-#ifndef NID_brainpoolP512r1
-#define NID_brainpoolP512r1 933
-#endif
-
-// NIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
-// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-static const int tlsNamedCurveNIDs[] = {
- // RFC 4492
- NID_sect163k1,
- NID_sect163r1,
- NID_sect163r2,
- NID_sect193r1,
- NID_sect193r2,
- NID_sect233k1,
- NID_sect233r1,
- NID_sect239k1,
- NID_sect283k1,
- NID_sect283r1,
- NID_sect409k1,
- NID_sect409r1,
- NID_sect571k1,
- NID_sect571r1,
-
- NID_secp160k1,
- NID_secp160r1,
- NID_secp160r2,
- NID_secp192k1,
- NID_X9_62_prime192v1, // secp192r1
- NID_secp224k1,
- NID_secp224r1,
- NID_secp256k1,
- NID_X9_62_prime256v1, // secp256r1
- NID_secp384r1,
- NID_secp521r1,
-
- // RFC 7027
- NID_brainpoolP256r1,
- NID_brainpoolP384r1,
- NID_brainpoolP512r1
-};
-
-static const size_t tlsNamedCurveNIDCount = sizeof(tlsNamedCurveNIDs) / sizeof(tlsNamedCurveNIDs[0]);
-
-bool QSslEllipticCurve::isTlsNamedCurve() const noexcept
-{
- const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
- return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index cdc018a508..241e6291ac 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -111,6 +75,16 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_SSL
+QT_IMPL_METATYPE_EXTERN_TAGGED(QList<QSslError>, QList_QSslError)
+#endif
+
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+// Avoid an ABI break due to the QScopedPointer->std::unique_ptr change
+static_assert(sizeof(QScopedPointer<QSslErrorPrivate>) == sizeof(std::unique_ptr<QSslErrorPrivate>));
+#endif
+
class QSslErrorPrivate
{
public:
@@ -163,7 +137,7 @@ QSslError::QSslError(SslError error, const QSslCertificate &certificate)
QSslError::QSslError(const QSslError &other)
: d(new QSslErrorPrivate)
{
- *d.data() = *other.d.data();
+ *d.get() = *other.d.get();
}
/*!
@@ -180,7 +154,7 @@ QSslError::~QSslError()
*/
QSslError &QSslError::operator=(const QSslError &other)
{
- *d.data() = *other.d.data();
+ *d.get() = *other.d.get();
return *this;
}
@@ -362,7 +336,7 @@ QSslCertificate QSslError::certificate() const
\since 5.4
\relates QHash
*/
-uint qHash(const QSslError &key, uint seed) noexcept
+size_t qHash(const QSslError &key, size_t seed) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.error());
@@ -385,3 +359,5 @@ QDebug operator<<(QDebug debug, const QSslError::SslError &error)
#endif
QT_END_NAMESPACE
+
+#include "moc_qsslerror.cpp"
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index 28eb1a9ea8..d82b086d39 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLERROR_H
@@ -45,6 +9,8 @@
#include <QtCore/qvariant.h>
#include <QtNetwork/qsslcertificate.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
@@ -100,13 +66,13 @@ public:
// RVCT compiler in debug build does not like about default values in const-
// So as an workaround we define all constructor overloads here explicitly
QSslError();
- QSslError(SslError error);
+ explicit QSslError(SslError error);
QSslError(SslError error, const QSslCertificate &certificate);
QSslError(const QSslError &other);
void swap(QSslError &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
~QSslError();
QSslError &operator=(QSslError &&other) noexcept { swap(other); return *this; }
@@ -120,24 +86,26 @@ public:
QSslCertificate certificate() const;
private:
- QScopedPointer<QSslErrorPrivate> d;
+ // ### Qt 7: make QSslError implicitly shared
+ std::unique_ptr<QSslErrorPrivate> d;
};
Q_DECLARE_SHARED(QSslError)
-Q_NETWORK_EXPORT uint qHash(const QSslError &key, uint seed = 0) noexcept;
+Q_NETWORK_EXPORT size_t qHash(const QSslError &key, size_t seed = 0) noexcept;
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError &error);
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError::SslError &error);
#endif
-
+#else
+class Q_NETWORK_EXPORT QSslError {}; // dummy class so that moc has a complete type
#endif // QT_NO_SSL
QT_END_NAMESPACE
#ifndef QT_NO_SSL
-Q_DECLARE_METATYPE(QList<QSslError>)
+QT_DECL_METATYPE_EXTERN_TAGGED(QList<QSslError>, QList_QSslError, Q_NETWORK_EXPORT)
#endif
#endif
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 74be406539..decfc4b5a1 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLKEY_H
@@ -44,7 +8,7 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qsharedpointer.h>
+#include <QtCore/qshareddata.h>
#include <QtNetwork/qssl.h>
QT_BEGIN_NAMESPACE
@@ -52,8 +16,6 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_SSL
-template <typename A, typename B> struct QPair;
-
class QIODevice;
class QSslKeyPrivate;
@@ -76,7 +38,7 @@ public:
QSslKey &operator=(const QSslKey &other);
~QSslKey();
- void swap(QSslKey &other) noexcept { qSwap(d, other.d); }
+ void swap(QSslKey &other) noexcept { d.swap(other.d); }
bool isNull() const;
void clear();
@@ -86,6 +48,7 @@ public:
QSsl::KeyAlgorithm algorithm() const;
QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const;
+ // ### Qt 7: drop passPhrase
QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const;
Qt::HANDLE handle() const;
@@ -95,8 +58,7 @@ public:
private:
QExplicitlySharedDataPointer<QSslKeyPrivate> d;
- friend class QSslCertificate;
- friend class QSslSocketBackendPrivate;
+ friend class QTlsBackend;
};
Q_DECLARE_SHARED(QSslKey)
diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp
deleted file mode 100644
index 814fe1c4bc..0000000000
--- a/src/network/ssl/qsslkey_mac.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-
-#include <CommonCrypto/CommonCrypto.h>
-
-#include <cstddef>
-
-QT_BEGIN_NAMESPACE
-
-static QByteArray wrapCCCrypt(CCOperation ccOp,
- QSslKeyPrivate::Cipher cipher,
- const QByteArray &data,
- const QByteArray &key, const QByteArray &iv)
-{
- int blockSize;
- CCAlgorithm ccAlgorithm;
- switch (cipher) {
- case QSslKeyPrivate::DesCbc:
- blockSize = kCCBlockSizeDES;
- ccAlgorithm = kCCAlgorithmDES;
- break;
- case QSslKeyPrivate::DesEde3Cbc:
- blockSize = kCCBlockSize3DES;
- ccAlgorithm = kCCAlgorithm3DES;
- break;
- case QSslKeyPrivate::Rc2Cbc:
- blockSize = kCCBlockSizeRC2;
- ccAlgorithm = kCCAlgorithmRC2;
- break;
- case QSslKeyPrivate::Aes128Cbc:
- case QSslKeyPrivate::Aes192Cbc:
- case QSslKeyPrivate::Aes256Cbc:
- blockSize = kCCBlockSizeAES128;
- ccAlgorithm = kCCAlgorithmAES;
- break;
- }
- size_t plainLength = 0;
- QByteArray plain(data.size() + blockSize, 0);
- CCCryptorStatus status = CCCrypt(
- ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
- key.constData(), std::size_t(key.size()),
- iv.constData(),
- data.constData(), std::size_t(data.size()),
- plain.data(), std::size_t(plain.size()), &plainLength);
- if (status == kCCSuccess)
- return plain.left(int(plainLength));
- return QByteArray();
-}
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return wrapCCCrypt(kCCDecrypt, cipher, data, key, iv);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return wrapCCCrypt(kCCEncrypt, cipher, data, key, iv);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
deleted file mode 100644
index 43cb8c6de8..0000000000
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslsocket.h"
-#include "qsslsocket_p.h"
-
-#include <QtCore/qatomic.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qiodevice.h>
-#ifndef QT_NO_DEBUG_STREAM
-#include <QtCore/qdebug.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-void QSslKeyPrivate::clear(bool deep)
-{
- isNull = true;
- if (!QSslSocket::supportsSsl())
- return;
- if (algorithm == QSsl::Rsa && rsa) {
- if (deep)
- q_RSA_free(rsa);
- rsa = nullptr;
- }
- if (algorithm == QSsl::Dsa && dsa) {
- if (deep)
- q_DSA_free(dsa);
- dsa = nullptr;
- }
- if (algorithm == QSsl::Dh && dh) {
- if (deep)
- q_DH_free(dh);
- dh = nullptr;
- }
-#ifndef OPENSSL_NO_EC
- if (algorithm == QSsl::Ec && ec) {
- if (deep)
- q_EC_KEY_free(ec);
- ec = nullptr;
- }
-#endif
- if (algorithm == QSsl::Opaque && opaque) {
- if (deep)
- q_EVP_PKEY_free(opaque);
- opaque = nullptr;
- }
-}
-
-bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
-{
- if (pkey == nullptr)
- return false;
-
- const int keyType = q_EVP_PKEY_type(q_EVP_PKEY_base_id(pkey));
- if (keyType == EVP_PKEY_RSA) {
- isNull = false;
- algorithm = QSsl::Rsa;
- type = QSsl::PrivateKey;
- rsa = q_EVP_PKEY_get1_RSA(pkey);
- return true;
- } else if (keyType == EVP_PKEY_DSA) {
- isNull = false;
- algorithm = QSsl::Dsa;
- type = QSsl::PrivateKey;
- dsa = q_EVP_PKEY_get1_DSA(pkey);
- return true;
- } else if (keyType == EVP_PKEY_DH) {
- isNull = false;
- algorithm = QSsl::Dh;
- type = QSsl::PrivateKey;
- dh = q_EVP_PKEY_get1_DH(pkey);
- return true;
- }
-#ifndef OPENSSL_NO_EC
- else if (keyType == EVP_PKEY_EC) {
- isNull = false;
- algorithm = QSsl::Ec;
- type = QSsl::PrivateKey;
- ec = q_EVP_PKEY_get1_EC_KEY(pkey);
- return true;
- }
-#endif
- else {
- // Unknown key type. This could be handled as opaque, but then
- // we'd eventually leak memory since we wouldn't be able to free
- // the underlying EVP_PKEY structure. For now, we won't support
- // this.
- }
-
- return false;
-}
-
-void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
-{
- QMap<QByteArray, QByteArray> headers;
- decodePem(pemFromDer(der, headers), passPhrase, deepClear);
-}
-
-void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
- bool deepClear)
-{
- if (pem.isEmpty())
- return;
-
- clear(deepClear);
-
- if (!QSslSocket::supportsSsl())
- return;
-
- BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
- if (!bio)
- return;
-
- void *phrase = const_cast<char *>(passPhrase.constData());
-
- if (algorithm == QSsl::Rsa) {
- RSA *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, nullptr, phrase)
- : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, phrase);
- if (rsa && rsa == result)
- isNull = false;
- } else if (algorithm == QSsl::Dsa) {
- DSA *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, nullptr, phrase)
- : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, nullptr, phrase);
- if (dsa && dsa == result)
- isNull = false;
- } else if (algorithm == QSsl::Dh) {
- EVP_PKEY *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_PUBKEY(bio, nullptr, nullptr, phrase)
- : q_PEM_read_bio_PrivateKey(bio, nullptr, nullptr, phrase);
- if (result)
- dh = q_EVP_PKEY_get1_DH(result);
- if (dh)
- isNull = false;
- q_EVP_PKEY_free(result);
-#ifndef OPENSSL_NO_EC
- } else if (algorithm == QSsl::Ec) {
- EC_KEY *result = (type == QSsl::PublicKey)
- ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, nullptr, phrase)
- : q_PEM_read_bio_ECPrivateKey(bio, &ec, nullptr, phrase);
- if (ec && ec == result)
- isNull = false;
-#endif
- }
-
- q_BIO_free(bio);
-}
-
-int QSslKeyPrivate::length() const
-{
- if (isNull || algorithm == QSsl::Opaque)
- return -1;
-
- switch (algorithm) {
- case QSsl::Rsa: return q_RSA_bits(rsa);
- case QSsl::Dsa: return q_DSA_bits(dsa);
- case QSsl::Dh: return q_DH_bits(dh);
-#ifndef OPENSSL_NO_EC
- case QSsl::Ec: return q_EC_GROUP_get_degree(q_EC_KEY_get0_group(ec));
-#endif
- default: return -1;
- }
-}
-
-QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
-{
- if (!QSslSocket::supportsSsl() || isNull || algorithm == QSsl::Opaque)
- return QByteArray();
-
- // ### the cipher should be selectable in the API:
- const EVP_CIPHER *cipher = nullptr;
- if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
-#ifndef OPENSSL_NO_DES
- cipher = q_EVP_des_ede3_cbc();
-#else
- return QByteArray();
-#endif
- }
-
- BIO *bio = q_BIO_new(q_BIO_s_mem());
- if (!bio)
- return QByteArray();
-
- bool fail = false;
-
- if (algorithm == QSsl::Rsa) {
- if (type == QSsl::PublicKey) {
- if (!q_PEM_write_bio_RSA_PUBKEY(bio, rsa))
- fail = true;
- } else {
- if (!q_PEM_write_bio_RSAPrivateKey(
- bio, rsa, cipher, (uchar *)passPhrase.data(),
- passPhrase.size(), nullptr, nullptr)) {
- fail = true;
- }
- }
- } else if (algorithm == QSsl::Dsa) {
- if (type == QSsl::PublicKey) {
- if (!q_PEM_write_bio_DSA_PUBKEY(bio, dsa))
- fail = true;
- } else {
- if (!q_PEM_write_bio_DSAPrivateKey(
- bio, dsa, cipher, (uchar *)passPhrase.data(),
- passPhrase.size(), nullptr, nullptr)) {
- fail = true;
- }
- }
- } else if (algorithm == QSsl::Dh) {
- EVP_PKEY *result = q_EVP_PKEY_new();
- if (!result || !q_EVP_PKEY_set1_DH(result, dh)) {
- fail = true;
- } else if (type == QSsl::PublicKey) {
- if (!q_PEM_write_bio_PUBKEY(bio, result))
- fail = true;
- } else if (!q_PEM_write_bio_PrivateKey(
- bio, result, cipher, (uchar *)passPhrase.data(),
- passPhrase.size(), nullptr, nullptr)) {
- fail = true;
- }
- q_EVP_PKEY_free(result);
-#ifndef OPENSSL_NO_EC
- } else if (algorithm == QSsl::Ec) {
- if (type == QSsl::PublicKey) {
- if (!q_PEM_write_bio_EC_PUBKEY(bio, ec))
- fail = true;
- } else {
- if (!q_PEM_write_bio_ECPrivateKey(
- bio, ec, cipher, (uchar *)passPhrase.data(),
- passPhrase.size(), nullptr, nullptr)) {
- fail = true;
- }
- }
-#endif
- } else {
- fail = true;
- }
-
- QByteArray pem;
- if (!fail) {
- char *data;
- long size = q_BIO_get_mem_data(bio, &data);
- pem = QByteArray(data, size);
- }
- q_BIO_free(bio);
- return pem;
-}
-
-Qt::HANDLE QSslKeyPrivate::handle() const
-{
- switch (algorithm) {
- case QSsl::Opaque:
- return Qt::HANDLE(opaque);
- case QSsl::Rsa:
- return Qt::HANDLE(rsa);
- case QSsl::Dsa:
- return Qt::HANDLE(dsa);
- case QSsl::Dh:
- return Qt::HANDLE(dh);
-#ifndef OPENSSL_NO_EC
- case QSsl::Ec:
- return Qt::HANDLE(ec);
-#endif
- default:
- return Qt::HANDLE(nullptr);
- }
-}
-
-static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv, int enc)
-{
- const EVP_CIPHER* type = nullptr;
- int i = 0, len = 0;
-
- switch (cipher) {
- case QSslKeyPrivate::DesCbc:
-#ifndef OPENSSL_NO_DES
- type = q_EVP_des_cbc();
-#endif
- break;
- case QSslKeyPrivate::DesEde3Cbc:
-#ifndef OPENSSL_NO_DES
- type = q_EVP_des_ede3_cbc();
-#endif
- break;
- case QSslKeyPrivate::Rc2Cbc:
-#ifndef OPENSSL_NO_RC2
- type = q_EVP_rc2_cbc();
-#endif
- break;
- case QSslKeyPrivate::Aes128Cbc:
- type = q_EVP_aes_128_cbc();
- break;
- case QSslKeyPrivate::Aes192Cbc:
- type = q_EVP_aes_192_cbc();
- break;
- case QSslKeyPrivate::Aes256Cbc:
- type = q_EVP_aes_256_cbc();
- break;
- }
-
- if (type == nullptr)
- return QByteArray();
-
- QByteArray output;
- output.resize(data.size() + EVP_MAX_BLOCK_LENGTH);
-
- EVP_CIPHER_CTX *ctx = q_EVP_CIPHER_CTX_new();
- q_EVP_CIPHER_CTX_reset(ctx);
- q_EVP_CipherInit(ctx, type, nullptr, nullptr, enc);
- q_EVP_CIPHER_CTX_set_key_length(ctx, key.size());
- if (cipher == QSslKeyPrivate::Rc2Cbc)
- q_EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_RC2_KEY_BITS, 8 * key.size(), nullptr);
-
- q_EVP_CipherInit_ex(ctx, nullptr, nullptr,
- reinterpret_cast<const unsigned char *>(key.constData()),
- reinterpret_cast<const unsigned char *>(iv.constData()),
- enc);
- q_EVP_CipherUpdate(ctx,
- reinterpret_cast<unsigned char *>(output.data()), &len,
- reinterpret_cast<const unsigned char *>(data.constData()), data.size());
- q_EVP_CipherFinal(ctx,
- reinterpret_cast<unsigned char *>(output.data()) + len, &i);
- len += i;
-
- q_EVP_CIPHER_CTX_reset(ctx);
- q_EVP_CIPHER_CTX_free(ctx);
-
- return output.left(len);
-}
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, 0);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, 1);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index b0d6c729f9..55cb2b0436 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
/*!
@@ -54,14 +18,12 @@
\sa QSslSocket, QSslCertificate, QSslCipher
*/
+#include "qssl_p.h"
#include "qsslkey.h"
#include "qsslkey_p.h"
-#ifndef QT_NO_OPENSSL
-#include "qsslsocket_openssl_symbols_p.h"
-#endif
#include "qsslsocket.h"
#include "qsslsocket_p.h"
-#include "qasn1element_p.h"
+#include "qtlsbackend_p.h"
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
@@ -73,11 +35,6 @@
QT_BEGIN_NAMESPACE
/*!
- \fn void QSslKeyPrivate::clear(bool deep)
- \internal
- */
-
-/*!
\fn void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
\internal
@@ -94,214 +51,57 @@ QT_BEGIN_NAMESPACE
*/
/*!
- Constructs a null key.
-
- \sa isNull()
-*/
-QSslKey::QSslKey()
- : d(new QSslKeyPrivate)
-{
-}
-
-/*!
\internal
*/
-QByteArray QSslKeyPrivate::pemHeader() const
+QSslKeyPrivate::QSslKeyPrivate()
{
- if (type == QSsl::PublicKey)
- return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
- else if (algorithm == QSsl::Rsa)
- return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
- else if (algorithm == QSsl::Dsa)
- return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
- else if (algorithm == QSsl::Ec)
- return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
- else if (algorithm == QSsl::Dh)
- return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
-
- Q_UNREACHABLE();
- return QByteArray();
-}
-
-static QByteArray pkcs8Header(bool encrypted)
-{
- return encrypted
- ? QByteArrayLiteral("-----BEGIN ENCRYPTED PRIVATE KEY-----")
- : QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
+ const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse();
+ if (!tlsBackend)
+ return;
+ backend.reset(tlsBackend->createKey());
+ if (backend.get())
+ backend->clear(false /*not deep clear*/);
+ else
+ qCWarning(lcSsl, "Active TLS backend does not support key creation");
}
/*!
\internal
*/
-QByteArray QSslKeyPrivate::pemFooter() const
+QSslKeyPrivate::~QSslKeyPrivate()
{
- if (type == QSsl::PublicKey)
- return QByteArrayLiteral("-----END PUBLIC KEY-----");
- else if (algorithm == QSsl::Rsa)
- return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
- else if (algorithm == QSsl::Dsa)
- return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
- else if (algorithm == QSsl::Ec)
- return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
- else if (algorithm == QSsl::Dh)
- return QByteArrayLiteral("-----END PRIVATE KEY-----");
-
- Q_UNREACHABLE();
- return QByteArray();
+ if (backend.get())
+ backend->clear(true /*deep clear*/);
}
-static QByteArray pkcs8Footer(bool encrypted)
+QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
- return encrypted
- ? QByteArrayLiteral("-----END ENCRYPTED PRIVATE KEY-----")
- : QByteArrayLiteral("-----END PRIVATE KEY-----");
-}
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
+ const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey());
+ return cryptor->decrypt(cipher, data, key, iv);
+ }
-/*!
- \internal
+ return {};
+}
- Returns a DER key formatted as PEM.
-*/
-QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
+QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
{
- QByteArray pem(der.toBase64());
-
- const int lineWidth = 64; // RFC 1421
- const int newLines = pem.size() / lineWidth;
- const bool rem = pem.size() % lineWidth;
-
- // ### optimize
- for (int i = 0; i < newLines; ++i)
- pem.insert((i + 1) * lineWidth + i, '\n');
- if (rem)
- pem.append('\n'); // ###
-
- QByteArray extra;
- if (!headers.isEmpty()) {
- QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
- do {
- --it;
- extra += it.key() + ": " + it.value() + '\n';
- } while (it != headers.constBegin());
- extra += '\n';
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse()) {
+ const std::unique_ptr<QTlsPrivate::TlsKey> cryptor(tlsBackend->createKey());
+ return cryptor->encrypt(cipher, data, key, iv);
}
- if (isEncryptedPkcs8(der)) {
- pem.prepend(pkcs8Header(true) + '\n' + extra);
- pem.append(pkcs8Footer(true) + '\n');
-#if !QT_CONFIG(openssl)
- } else if (isPkcs8) {
- pem.prepend(pkcs8Header(false) + '\n' + extra);
- pem.append(pkcs8Footer(false) + '\n');
-#endif
- } else {
- pem.prepend(pemHeader() + '\n' + extra);
- pem.append(pemFooter() + '\n');
- }
-
- return pem;
+ return {};
}
/*!
- \internal
+ Constructs a null key.
- Returns a PEM key formatted as DER.
+ \sa isNull()
*/
-QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
-{
- QByteArray header = pemHeader();
- QByteArray footer = pemFooter();
-
- QByteArray der(pem);
-
- int headerIndex = der.indexOf(header);
- int footerIndex = der.indexOf(footer, headerIndex + header.length());
- if (type != QSsl::PublicKey) {
- if (headerIndex == -1 || footerIndex == -1) {
- header = pkcs8Header(true);
- footer = pkcs8Footer(true);
- headerIndex = der.indexOf(header);
- footerIndex = der.indexOf(footer, headerIndex + header.length());
- }
- if (headerIndex == -1 || footerIndex == -1) {
- header = pkcs8Header(false);
- footer = pkcs8Footer(false);
- headerIndex = der.indexOf(header);
- footerIndex = der.indexOf(footer, headerIndex + header.length());
- }
- }
- if (headerIndex == -1 || footerIndex == -1)
- return QByteArray();
-
- der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
-
- if (der.contains("Proc-Type:")) {
- // taken from QHttpNetworkReplyPrivate::parseHeader
- int i = 0;
- while (i < der.count()) {
- int j = der.indexOf(':', i); // field-name
- if (j == -1)
- break;
- const QByteArray field = der.mid(i, j - i).trimmed();
- j++;
- // any number of LWS is allowed before and after the value
- QByteArray value;
- do {
- i = der.indexOf('\n', j);
- if (i == -1)
- break;
- if (!value.isEmpty())
- value += ' ';
- // check if we have CRLF or only LF
- bool hasCR = (i && der[i-1] == '\r');
- int length = i -(hasCR ? 1: 0) - j;
- value += der.mid(j, length).trimmed();
- j = ++i;
- } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
- if (i == -1)
- break; // something is wrong
-
- headers->insert(field, value);
- }
- der = der.mid(i);
- }
-
- return QByteArray::fromBase64(der); // ignores newlines
-}
-
-bool QSslKeyPrivate::isEncryptedPkcs8(const QByteArray &der) const
+QSslKey::QSslKey()
+ : d(new QSslKeyPrivate)
{
- static const QVector<QByteArray> pbes1OIds {
- // PKCS5
- {PKCS5_MD2_DES_CBC_OID},
- {PKCS5_MD2_RC2_CBC_OID},
- {PKCS5_MD5_DES_CBC_OID},
- {PKCS5_MD5_RC2_CBC_OID},
- {PKCS5_SHA1_DES_CBC_OID},
- {PKCS5_SHA1_RC2_CBC_OID},
- };
- QAsn1Element elem;
- if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- const QVector<QAsn1Element> items = elem.toVector();
- if (items.size() != 2
- || items[0].type() != QAsn1Element::SequenceType
- || items[1].type() != QAsn1Element::OctetStringType) {
- return false;
- }
-
- const QVector<QAsn1Element> encryptionSchemeContainer = items[0].toVector();
- if (encryptionSchemeContainer.size() != 2
- || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
- || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
- return false;
- }
-
- const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
- return encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID
- || pbes1OIds.contains(encryptionScheme)
- || encryptionScheme.startsWith(PKCS12_OID);
}
/*!
@@ -318,12 +118,12 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
: d(new QSslKeyPrivate)
{
- d->type = type;
- d->algorithm = algorithm;
- if (encoding == QSsl::Der)
- d->decodeDer(encoded, passPhrase);
- else
- d->decodePem(encoded, passPhrase);
+ if (auto *tlsKey = d->backend.get()) {
+ if (encoding == QSsl::Der)
+ tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ else
+ tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ }
}
/*!
@@ -343,12 +143,13 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QByteArray encoded;
if (device)
encoded = device->readAll();
- d->type = type;
- d->algorithm = algorithm;
- if (encoding == QSsl::Der)
- d->decodeDer(encoded, passPhrase);
- else
- d->decodePem(encoded, passPhrase);
+
+ if (auto *tlsKey = d->backend.get()) {
+ if (encoding == QSsl::Der)
+ tlsKey->decodeDer(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ else
+ tlsKey->decodePem(type, algorithm, encoded, passPhrase, true /*deep clear*/);
+ }
}
/*!
@@ -362,20 +163,8 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
: d(new QSslKeyPrivate)
{
-#ifndef QT_NO_OPENSSL
- EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
- if (!evpKey || !d->fromEVP_PKEY(evpKey)) {
- d->opaque = evpKey;
- d->algorithm = QSsl::Opaque;
- } else {
- q_EVP_PKEY_free(evpKey);
- }
-#else
- d->opaque = handle;
- d->algorithm = QSsl::Opaque;
-#endif
- d->type = type;
- d->isNull = !d->opaque;
+ if (auto *tlsKey = d->backend.get())
+ tlsKey->fromHandle(handle, type);
}
/*!
@@ -437,7 +226,10 @@ QSslKey &QSslKey::operator=(const QSslKey &other)
*/
bool QSslKey::isNull() const
{
- return d->isNull;
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->isNull();
+
+ return true;
}
/*!
@@ -455,7 +247,10 @@ void QSslKey::clear()
*/
int QSslKey::length() const
{
- return d->length();
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->length();
+
+ return -1;
}
/*!
@@ -463,7 +258,10 @@ int QSslKey::length() const
*/
QSsl::KeyType QSslKey::type() const
{
- return d->type;
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->type();
+
+ return QSsl::PublicKey;
}
/*!
@@ -471,7 +269,10 @@ QSsl::KeyType QSslKey::type() const
*/
QSsl::KeyAlgorithm QSslKey::algorithm() const
{
- return d->algorithm;
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->algorithm();
+
+ return QSsl::Opaque;
}
/*!
@@ -482,19 +283,18 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const
*/
QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
{
- if (d->isNull || d->algorithm == QSsl::Opaque)
- return QByteArray();
+ if (isNull() || algorithm() == QSsl::Opaque)
+ return {};
// Encrypted DER is nonsense, see QTBUG-41038.
- if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty())
- return QByteArray();
+ if (type() == QSsl::PrivateKey && !passPhrase.isEmpty())
+ return {};
-#ifndef QT_NO_OPENSSL
QMap<QByteArray, QByteArray> headers;
- return d->derFromPem(toPem(passPhrase), &headers);
-#else
- return d->derData;
-#endif
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->derFromPem(toPem(passPhrase), &headers);
+
+ return {};
}
/*!
@@ -504,7 +304,10 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
*/
QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
{
- return d->toPem(passPhrase);
+ if (const auto *tlsKey = d->backend.get())
+ return tlsKey->toPem(passPhrase);
+
+ return {};
}
/*!
@@ -520,7 +323,10 @@ QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
*/
Qt::HANDLE QSslKey::handle() const
{
- return d->handle();
+ if (d->backend.get())
+ return d->backend->handle();
+
+ return nullptr;
}
/*!
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index dd1a31b0e5..d28ee5ad11 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLKEY_OPENSSL_P_H
@@ -53,83 +17,30 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#include "qsslkey.h"
-#include "qsslsocket_p.h" // includes wincrypt.h
+#include "qssl_p.h"
-#ifndef QT_NO_OPENSSL
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#endif
+#include <memory>
QT_BEGIN_NAMESPACE
+namespace QTlsPrivate {
+class TlsKey;
+}
+
class QSslKeyPrivate
{
public:
- inline QSslKeyPrivate()
- : algorithm(QSsl::Opaque)
- , opaque(nullptr)
- {
- clear(false);
- }
-
- inline ~QSslKeyPrivate()
- { clear(); }
-
- void clear(bool deep = true);
-
-#ifndef QT_NO_OPENSSL
- bool fromEVP_PKEY(EVP_PKEY *pkey);
-#endif
- void decodeDer(const QByteArray &der, const QByteArray &passPhrase = {}, bool deepClear = true);
- void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true);
- QByteArray pemHeader() const;
- QByteArray pemFooter() const;
- QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const;
- QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const;
-
- int length() const;
- QByteArray toPem(const QByteArray &passPhrase) const;
- Qt::HANDLE handle() const;
-
- bool isEncryptedPkcs8(const QByteArray &der) const;
-#if !QT_CONFIG(openssl)
- QByteArray decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase);
- bool isPkcs8 = false;
-#endif
-
- bool isNull;
- QSsl::KeyType type;
- QSsl::KeyAlgorithm algorithm;
-
- enum Cipher {
- DesCbc,
- DesEde3Cbc,
- Rc2Cbc,
- Aes128Cbc,
- Aes192Cbc,
- Aes256Cbc
- };
+ QSslKeyPrivate();
+ ~QSslKeyPrivate();
- Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
- Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
+ using Cipher = QTlsPrivate::Cipher;
-#ifndef QT_NO_OPENSSL
- union {
- EVP_PKEY *opaque;
- RSA *rsa;
- DSA *dsa;
- DH *dh;
-#ifndef OPENSSL_NO_EC
- EC_KEY *ec;
-#endif
- };
-#else
- Qt::HANDLE opaque;
- QByteArray derData;
- int keyLength;
-#endif
+ Q_NETWORK_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
+ Q_NETWORK_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
+ std::unique_ptr<QTlsPrivate::TlsKey> backend;
QAtomicInt ref;
private:
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
deleted file mode 100644
index 43969c3d28..0000000000
--- a/src/network/ssl/qsslkey_qt.cpp
+++ /dev/null
@@ -1,789 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qasn1element_p.h"
-
-#include <QtCore/qdatastream.h>
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/QMessageAuthenticationCode>
-#include <QtCore/qrandom.h>
-
-#include <QtNetwork/qpassworddigestor.h>
-
-#include <cstring>
-
-QT_USE_NAMESPACE
-
-static const quint8 bits_table[256] = {
- 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
- 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
-};
-
-// OIDs of named curves allowed in TLS as per RFCs 4492 and 7027,
-// see also https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
-
-typedef QMap<QByteArray, int> OidLengthMap;
-static OidLengthMap createOidMap()
-{
- OidLengthMap oids;
- oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.1"), 192); // secp192r1 a.k.a prime192v1
- oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.10045.3.1.7"), 256); // secp256r1 a.k.a prime256v1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.1"), 193); // sect193r2
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.10"), 256); // secp256k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.16"), 283); // sect283k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.17"), 283); // sect283r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.26"), 233); // sect233k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.27"), 233); // sect233r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.3"), 239); // sect239k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.30"), 160); // secp160r2
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.31"), 192); // secp192k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.32"), 224); // secp224k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.33"), 224); // secp224r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.34"), 384); // secp384r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.35"), 521); // secp521r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.36"), 409); // sect409k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.37"), 409); // sect409r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.38"), 571); // sect571k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.39"), 571); // sect571r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.8"), 160); // secp160r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.132.0.9"), 160); // secp160k1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.11"), 384); // brainpoolP384r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.13"), 512); // brainpoolP512r1
- oids.insert(oids.cend(), QByteArrayLiteral("1.3.36.3.3.2.8.1.1.7"), 256); // brainpoolP256r1
- return oids;
-}
-Q_GLOBAL_STATIC_WITH_ARGS(OidLengthMap, oidLengthMap, (createOidMap()))
-
-static int curveBits(const QByteArray &oid)
-{
- const int length = oidLengthMap->value(oid);
- return length ? length : -1;
-}
-
-static int numberOfBits(const QByteArray &modulus)
-{
- int bits = modulus.size() * 8;
- for (int i = 0; i < modulus.size(); ++i) {
- quint8 b = modulus[i];
- bits -= 8;
- if (b != 0) {
- bits += bits_table[b];
- break;
- }
- }
- return bits;
-}
-
-static QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
-{
- // This is somewhat simplified and shortened version of what OpenSSL does.
- // See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
- // in their code for what they pass as arguments to EVP_BytesToKey when
- // deriving encryption keys (when reading/writing pems files with encrypted
- // keys).
-
- Q_ASSERT(iv.size() >= 8);
-
- QCryptographicHash hash(QCryptographicHash::Md5);
-
- QByteArray data(passPhrase);
- data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
-
- hash.addData(data);
-
- if (cipher == QSslKeyPrivate::Aes128Cbc)
- return hash.result();
-
- QByteArray key(hash.result());
- hash.reset();
- hash.addData(key);
- hash.addData(data);
-
- if (cipher == QSslKeyPrivate::Aes192Cbc)
- return key.append(hash.result().constData(), 8);
-
- return key.append(hash.result());
-}
-
-static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
-{
- QByteArray key;
- QCryptographicHash hash(QCryptographicHash::Md5);
- hash.addData(passPhrase);
- hash.addData(iv);
- switch (cipher) {
- case QSslKeyPrivate::DesCbc:
- key = hash.result().left(8);
- break;
- case QSslKeyPrivate::DesEde3Cbc:
- key = hash.result();
- hash.reset();
- hash.addData(key);
- hash.addData(passPhrase);
- hash.addData(iv);
- key += hash.result().left(8);
- break;
- case QSslKeyPrivate::Rc2Cbc:
- key = hash.result();
- break;
- case QSslKeyPrivate::Aes128Cbc:
- case QSslKeyPrivate::Aes192Cbc:
- case QSslKeyPrivate::Aes256Cbc:
- return deriveAesKey(cipher, passPhrase, iv);
- }
- return key;
-}
-
-void QSslKeyPrivate::clear(bool deep)
-{
- isNull = true;
- if (deep)
- std::memset(derData.data(), 0, derData.size());
- derData.clear();
- keyLength = -1;
-}
-
-static int extractPkcs8KeyLength(const QVector<QAsn1Element> &items, QSslKeyPrivate *that) {
- Q_ASSERT(items.size() == 3);
- int keyLength;
-
- auto getName = [](QSsl::KeyAlgorithm algorithm) {
- switch (algorithm){
- case QSsl::Rsa: return "RSA";
- case QSsl::Dsa: return "DSA";
- case QSsl::Dh: return "DH";
- case QSsl::Ec: return "EC";
- case QSsl::Opaque: return "Opaque";
- }
- Q_UNREACHABLE();
- };
-
- const QVector<QAsn1Element> pkcs8Info = items[1].toVector();
- if (pkcs8Info.size() != 2 || pkcs8Info[0].type() != QAsn1Element::ObjectIdentifierType)
- return -1;
- const QByteArray value = pkcs8Info[0].toObjectId();
- if (value == RSA_ENCRYPTION_OID) {
- if (Q_UNLIKELY(that->algorithm != QSsl::Rsa)) {
- // We could change the 'algorithm' of QSslKey here and continue loading, but
- // this is not supported in the openssl back-end, so we'll fail here and give
- // the user some feedback.
- qWarning() << "QSslKey: Found RSA key when asked to use" << getName(that->algorithm)
- << "\nLoading will fail.";
- return -1;
- }
- // Luckily it contains the 'normal' RSA-key format inside, so we can just recurse
- // and read the key's info.
- that->decodeDer(items[2].value());
- // The real info has been filled out in the call above, so return as if it was invalid
- // to avoid overwriting the data.
- return -1;
- } else if (value == EC_ENCRYPTION_OID) {
- if (Q_UNLIKELY(that->algorithm != QSsl::Ec)) {
- // As above for RSA.
- qWarning() << "QSslKey: Found EC key when asked to use" << getName(that->algorithm)
- << "\nLoading will fail.";
- return -1;
- }
- // I don't know where this is documented, but the elliptic-curve identifier has been
- // moved into the "pkcs#8 wrapper", which is what we're interested in.
- if (pkcs8Info[1].type() != QAsn1Element::ObjectIdentifierType)
- return -1;
- keyLength = curveBits(pkcs8Info[1].toObjectId());
- } else if (value == DSA_ENCRYPTION_OID) {
- if (Q_UNLIKELY(that->algorithm != QSsl::Dsa)) {
- // As above for RSA.
- qWarning() << "QSslKey: Found DSA when asked to use" << getName(that->algorithm)
- << "\nLoading will fail.";
- return -1;
- }
- // DSA's structure is documented here:
- // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
- if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
- return -1;
- const QVector<QAsn1Element> dsaInfo = pkcs8Info[1].toVector();
- if (dsaInfo.size() != 3 || dsaInfo[0].type() != QAsn1Element::IntegerType)
- return -1;
- keyLength = numberOfBits(dsaInfo[0].value());
- } else if (value == DH_ENCRYPTION_OID) {
- if (Q_UNLIKELY(that->algorithm != QSsl::Dh)) {
- // As above for RSA.
- qWarning() << "QSslKey: Found DH when asked to use" << getName(that->algorithm)
- << "\nLoading will fail.";
- return -1;
- }
- // DH's structure is documented here:
- // https://www.cryptsoft.com/pkcs11doc/STANDARD/v201-95.pdf in section 11.9.
- if (pkcs8Info[1].type() != QAsn1Element::SequenceType)
- return -1;
- const QVector<QAsn1Element> dhInfo = pkcs8Info[1].toVector();
- if (dhInfo.size() < 2 || dhInfo.size() > 3 || dhInfo[0].type() != QAsn1Element::IntegerType)
- return -1;
- keyLength = numberOfBits(dhInfo[0].value());
- } else {
- // in case of unexpected formats:
- qWarning() << "QSslKey: Unsupported PKCS#8 key algorithm:" << value
- << "\nFile a bugreport to Qt (include the line above).";
- return -1;
- }
- return keyLength;
-}
-
-void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
-{
- clear(deepClear);
-
- if (der.isEmpty())
- return;
- // decryptPkcs8 decrypts if necessary or returns 'der' unaltered
- QByteArray decryptedDer = decryptPkcs8(der, passPhrase);
-
- QAsn1Element elem;
- if (!elem.read(decryptedDer) || elem.type() != QAsn1Element::SequenceType)
- return;
-
- if (type == QSsl::PublicKey) {
- // key info
- QDataStream keyStream(elem.value());
- if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
- return;
- const QVector<QAsn1Element> infoItems = elem.toVector();
- if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType)
- return;
- if (algorithm == QSsl::Rsa) {
- if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID)
- return;
- // key data
- if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty())
- return;
- if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType)
- return;
- if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(elem.value());
- } else if (algorithm == QSsl::Dsa) {
- if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID)
- return;
- if (infoItems[1].type() != QAsn1Element::SequenceType)
- return;
- // key params
- const QVector<QAsn1Element> params = infoItems[1].toVector();
- if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(params[0].value());
- } else if (algorithm == QSsl::Dh) {
- if (infoItems[0].toObjectId() != DH_ENCRYPTION_OID)
- return;
- if (infoItems[1].type() != QAsn1Element::SequenceType)
- return;
- // key params
- const QVector<QAsn1Element> params = infoItems[1].toVector();
- if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(params[0].value());
- } else if (algorithm == QSsl::Ec) {
- if (infoItems[0].toObjectId() != EC_ENCRYPTION_OID)
- return;
- if (infoItems[1].type() != QAsn1Element::ObjectIdentifierType)
- return;
- keyLength = curveBits(infoItems[1].toObjectId());
- }
-
- } else {
- const QVector<QAsn1Element> items = elem.toVector();
- if (items.isEmpty())
- return;
-
- // version
- if (items[0].type() != QAsn1Element::IntegerType)
- return;
- const QByteArray versionHex = items[0].value().toHex();
-
- if (items.size() == 3 && items[1].type() == QAsn1Element::SequenceType
- && items[2].type() == QAsn1Element::OctetStringType) {
- if (versionHex != "00" && versionHex != "01")
- return;
- int pkcs8KeyLength = extractPkcs8KeyLength(items, this);
- if (pkcs8KeyLength == -1)
- return;
- isPkcs8 = true;
- keyLength = pkcs8KeyLength;
- } else if (algorithm == QSsl::Rsa) {
- if (versionHex != "00")
- return;
- if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(items[1].value());
- } else if (algorithm == QSsl::Dsa) {
- if (versionHex != "00")
- return;
- if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(items[1].value());
- } else if (algorithm == QSsl::Dh) {
- if (versionHex != "00")
- return;
- if (items.size() < 5 || items.size() > 6 || items[1].type() != QAsn1Element::IntegerType)
- return;
- keyLength = numberOfBits(items[1].value());
- } else if (algorithm == QSsl::Ec) {
- if (versionHex != "01")
- return;
- if (items.size() != 4
- || items[1].type() != QAsn1Element::OctetStringType
- || items[2].type() != QAsn1Element::Context0Type
- || items[3].type() != QAsn1Element::Context1Type)
- return;
- QAsn1Element oidElem;
- if (!oidElem.read(items[2].value())
- || oidElem.type() != QAsn1Element::ObjectIdentifierType)
- return;
- keyLength = curveBits(oidElem.toObjectId());
- }
- }
-
- derData = decryptedDer;
- isNull = false;
-}
-
-void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
- bool deepClear)
-{
- QMap<QByteArray, QByteArray> headers;
- QByteArray data = derFromPem(pem, &headers);
- if (headers.value("Proc-Type") == "4,ENCRYPTED") {
- const QList<QByteArray> dekInfo = headers.value("DEK-Info").split(',');
- if (dekInfo.size() != 2) {
- clear(deepClear);
- return;
- }
-
- Cipher cipher;
- if (dekInfo.first() == "DES-CBC") {
- cipher = DesCbc;
- } else if (dekInfo.first() == "DES-EDE3-CBC") {
- cipher = DesEde3Cbc;
- } else if (dekInfo.first() == "RC2-CBC") {
- cipher = Rc2Cbc;
- } else if (dekInfo.first() == "AES-128-CBC") {
- cipher = Aes128Cbc;
- } else if (dekInfo.first() == "AES-192-CBC") {
- cipher = Aes192Cbc;
- } else if (dekInfo.first() == "AES-256-CBC") {
- cipher = Aes256Cbc;
- } else {
- clear(deepClear);
- return;
- }
-
- const QByteArray iv = QByteArray::fromHex(dekInfo.last());
- const QByteArray key = deriveKey(cipher, passPhrase, iv);
- data = decrypt(cipher, data, key, iv);
- }
- decodeDer(data, passPhrase, deepClear);
-}
-
-int QSslKeyPrivate::length() const
-{
- return keyLength;
-}
-
-QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
-{
- QByteArray data;
- QMap<QByteArray, QByteArray> headers;
-
- if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
- // ### use a cryptographically secure random number generator
- quint64 random = QRandomGenerator::system()->generate64();
- QByteArray iv = QByteArray::fromRawData(reinterpret_cast<const char *>(&random), sizeof(random));
-
- Cipher cipher = DesEde3Cbc;
- const QByteArray key = deriveKey(cipher, passPhrase, iv);
- data = encrypt(cipher, derData, key, iv);
-
- headers.insert("Proc-Type", "4,ENCRYPTED");
- headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex());
- } else {
- data = derData;
- }
-
- return pemFromDer(data, headers);
-}
-
-Qt::HANDLE QSslKeyPrivate::handle() const
-{
- return opaque;
-}
-
-// Maps OIDs to the encryption cipher they specify
-static const QMap<QByteArray, QSslKeyPrivate::Cipher> oidCipherMap {
- {DES_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesCbc},
- {DES_EDE3_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::DesEde3Cbc},
- // {PKCS5_MD2_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc}, // No MD2
- {PKCS5_MD5_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
- {PKCS5_SHA1_DES_CBC_OID, QSslKeyPrivate::Cipher::DesCbc},
- // {PKCS5_MD2_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc}, // No MD2
- {PKCS5_MD5_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
- {PKCS5_SHA1_RC2_CBC_OID, QSslKeyPrivate::Cipher::Rc2Cbc},
- {RC2_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc2Cbc}
- // {RC5_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Rc5Cbc}, // No RC5
- // {AES128_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes128}, // no AES
- // {AES192_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes192},
- // {AES256_CBC_ENCRYPTION_OID, QSslKeyPrivate::Cipher::Aes256}
-};
-
-struct EncryptionData
-{
- EncryptionData() : initialized(false)
- {}
- EncryptionData(QSslKeyPrivate::Cipher cipher, QByteArray key, QByteArray iv)
- : initialized(true), cipher(cipher), key(key), iv(iv)
- {}
- bool initialized;
- QSslKeyPrivate::Cipher cipher;
- QByteArray key;
- QByteArray iv;
-};
-
-static EncryptionData readPbes2(const QVector<QAsn1Element> &element, const QByteArray &passPhrase)
-{
- // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.2
- /*** Scheme: ***
- * Sequence (scheme-specific info..)
- * Sequence (key derivation info)
- * Object Identifier (Key derivation algorithm (e.g. PBKDF2))
- * Sequence (salt)
- * CHOICE (this entry can be either of the types it contains)
- * Octet string (actual salt)
- * Object identifier (Anything using this is deferred to a later version of PKCS #5)
- * Integer (iteration count)
- * Sequence (encryption algorithm info)
- * Object identifier (identifier for the algorithm)
- * Algorithm dependent, is covered in the switch further down
- */
-
- static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes2OidHashFunctionMap {
- // PBES2/PBKDF2
- {HMAC_WITH_SHA1, QCryptographicHash::Sha1},
- {HMAC_WITH_SHA224, QCryptographicHash::Sha224},
- {HMAC_WITH_SHA256, QCryptographicHash::Sha256},
- {HMAC_WITH_SHA512, QCryptographicHash::Sha512},
- {HMAC_WITH_SHA512_224, QCryptographicHash::Sha512},
- {HMAC_WITH_SHA512_256, QCryptographicHash::Sha512},
- {HMAC_WITH_SHA384, QCryptographicHash::Sha384}
- };
-
- // Values from their respective sections here: https://tools.ietf.org/html/rfc8018#appendix-B.2
- static const QMap<QSslKeyPrivate::Cipher, int> cipherKeyLengthMap {
- {QSslKeyPrivate::Cipher::DesCbc, 8},
- {QSslKeyPrivate::Cipher::DesEde3Cbc, 24},
- // @note: variable key-length (https://tools.ietf.org/html/rfc8018#appendix-B.2.3)
- {QSslKeyPrivate::Cipher::Rc2Cbc, 4}
- // @todo: AES(, rc5?)
- };
-
- const QVector<QAsn1Element> keyDerivationContainer = element[0].toVector();
- if (keyDerivationContainer.size() != 2
- || keyDerivationContainer[0].type() != QAsn1Element::ObjectIdentifierType
- || keyDerivationContainer[1].type() != QAsn1Element::SequenceType) {
- return {};
- }
-
- const QByteArray keyDerivationAlgorithm = keyDerivationContainer[0].toObjectId();
- const QVector<QAsn1Element> keyDerivationParams = keyDerivationContainer[1].toVector();
-
- const QVector<QAsn1Element> encryptionAlgorithmContainer = element[1].toVector();
- if (encryptionAlgorithmContainer.size() != 2
- || encryptionAlgorithmContainer[0].type() != QAsn1Element::ObjectIdentifierType) {
- return {};
- }
-
- auto iterator = oidCipherMap.constFind(encryptionAlgorithmContainer[0].toObjectId());
- if (iterator == oidCipherMap.cend()) {
- qWarning()
- << "QSslKey: Unsupported encryption cipher OID:" << encryptionAlgorithmContainer[0].toObjectId()
- << "\nFile a bugreport to Qt (include the line above).";
- return {};
- }
-
- QSslKeyPrivate::Cipher cipher = *iterator;
- QByteArray key;
- QByteArray iv;
- switch (cipher) {
- case QSslKeyPrivate::Cipher::DesCbc:
- case QSslKeyPrivate::Cipher::DesEde3Cbc:
- // https://tools.ietf.org/html/rfc8018#appendix-B.2.1 (DES-CBC-PAD)
- // https://tools.ietf.org/html/rfc8018#appendix-B.2.2 (DES-EDE3-CBC-PAD)
- // @todo https://tools.ietf.org/html/rfc8018#appendix-B.2.5 (AES-CBC-PAD)
- /*** Scheme: ***
- * Octet string (IV)
- */
- if (encryptionAlgorithmContainer[1].type() != QAsn1Element::OctetStringType)
- return {};
-
- // @note: All AES identifiers should be able to use this branch!!
- iv = encryptionAlgorithmContainer[1].value();
-
- if (iv.size() != 8) // @note: AES needs 16 bytes
- return {};
- break;
- case QSslKeyPrivate::Cipher::Rc2Cbc: {
- // https://tools.ietf.org/html/rfc8018#appendix-B.2.3
- /*** Scheme: ***
- * Sequence (rc2 parameters)
- * Integer (rc2 parameter version)
- * Octet string (IV)
- */
- if (encryptionAlgorithmContainer[1].type() != QAsn1Element::SequenceType)
- return {};
- const QVector<QAsn1Element> rc2ParametersContainer = encryptionAlgorithmContainer[1].toVector();
- if ((rc2ParametersContainer.size() != 1 && rc2ParametersContainer.size() != 2)
- || rc2ParametersContainer.back().type() != QAsn1Element::OctetStringType) {
- return {};
- }
- iv = rc2ParametersContainer.back().value();
- if (iv.size() != 8)
- return {};
- break;
- } // @todo(?): case (RC5 , AES)
- case QSslKeyPrivate::Cipher::Aes128Cbc:
- case QSslKeyPrivate::Cipher::Aes192Cbc:
- case QSslKeyPrivate::Cipher::Aes256Cbc:
- Q_UNREACHABLE();
- }
-
- if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {
- // Definition: https://tools.ietf.org/html/rfc8018#appendix-A.2
- QByteArray salt;
- if (keyDerivationParams[0].type() == QAsn1Element::OctetStringType) {
- salt = keyDerivationParams[0].value();
- } else if (keyDerivationParams[0].type() == QAsn1Element::ObjectIdentifierType) {
- Q_UNIMPLEMENTED();
- /* See paragraph from https://tools.ietf.org/html/rfc8018#appendix-A.2
- which ends with: "such facilities are deferred to a future version of PKCS #5"
- */
- return {};
- } else {
- return {};
- }
-
- // Iterations needed to derive the key
- int iterationCount = keyDerivationParams[1].toInteger();
- // Optional integer
- int keyLength = -1;
- int vectorPos = 2;
- if (keyDerivationParams.size() > vectorPos
- && keyDerivationParams[vectorPos].type() == QAsn1Element::IntegerType) {
- keyLength = keyDerivationParams[vectorPos].toInteger(nullptr);
- ++vectorPos;
- } else {
- keyLength = cipherKeyLengthMap[cipher];
- }
-
- // Optional algorithm identifier (default: HMAC-SHA-1)
- QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha1;
- if (keyDerivationParams.size() > vectorPos
- && keyDerivationParams[vectorPos].type() == QAsn1Element::SequenceType) {
- QVector<QAsn1Element> hashAlgorithmContainer = keyDerivationParams[vectorPos].toVector();
- hashAlgorithm = pbes2OidHashFunctionMap[hashAlgorithmContainer.front().toObjectId()];
- Q_ASSERT(hashAlgorithmContainer[1].type() == QAsn1Element::NullType);
- ++vectorPos;
- }
- Q_ASSERT(keyDerivationParams.size() == vectorPos);
-
- key = QPasswordDigestor::deriveKeyPbkdf2(hashAlgorithm, passPhrase, salt, iterationCount, keyLength);
- } else {
- qWarning()
- << "QSslKey: Unsupported key derivation algorithm OID:" << keyDerivationAlgorithm
- << "\nFile a bugreport to Qt (include the line above).";
- return {};
- }
- return {cipher, key, iv};
-}
-
-// Maps OIDs to the hash function it specifies
-static const QMap<QByteArray, QCryptographicHash::Algorithm> pbes1OidHashFunctionMap {
-#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
- // PKCS5
- //{PKCS5_MD2_DES_CBC_OID, QCryptographicHash::Md2}, No MD2
- //{PKCS5_MD2_RC2_CBC_OID, QCryptographicHash::Md2},
- {PKCS5_MD5_DES_CBC_OID, QCryptographicHash::Md5},
- {PKCS5_MD5_RC2_CBC_OID, QCryptographicHash::Md5},
-#endif
- {PKCS5_SHA1_DES_CBC_OID, QCryptographicHash::Sha1},
- {PKCS5_SHA1_RC2_CBC_OID, QCryptographicHash::Sha1},
- // PKCS12 (unimplemented)
- // {PKCS12_SHA1_RC4_128_OID, QCryptographicHash::Sha1}, // No RC4
- // {PKCS12_SHA1_RC4_40_OID, QCryptographicHash::Sha1},
- // @todo: lacking support. @note: there might be code to do this inside qsslsocket_mac...
- // further note that more work may be required for the 3DES variations listed to be available.
- // {PKCS12_SHA1_3KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
- // {PKCS12_SHA1_2KEY_3DES_CBC_OID, QCryptographicHash::Sha1},
- // {PKCS12_SHA1_RC2_128_CBC_OID, QCryptographicHash::Sha1},
- // {PKCS12_SHA1_RC2_40_CBC_OID, QCryptographicHash::Sha1}
-};
-
-
-static EncryptionData readPbes1(const QVector<QAsn1Element> &element, const QByteArray &encryptionScheme, const QByteArray &passPhrase)
-{
- // RFC 8018: https://tools.ietf.org/html/rfc8018#section-6.1
- // Steps refer to this section: https://tools.ietf.org/html/rfc8018#section-6.1.2
- /*** Scheme: ***
- * Sequence (PBE Parameter)
- * Octet string (salt)
- * Integer (iteration counter)
- */
- // Step 1
- if (element.size() != 2
- || element[0].type() != QAsn1Element::ElementType::OctetStringType
- || element[1].type() != QAsn1Element::ElementType::IntegerType) {
- return {};
- }
- QByteArray salt = element[0].value();
- if (salt.size() != 8)
- return {};
-
- int iterationCount = element[1].toInteger();
- if (iterationCount < 0)
- return {};
-
- // Step 2
- auto iterator = pbes1OidHashFunctionMap.constFind(encryptionScheme);
- if (iterator == pbes1OidHashFunctionMap.cend()) {
- // Qt was compiled with ONLY_SHA1 (or it's MD2)
- return {};
- }
- QCryptographicHash::Algorithm hashAlgorithm = *iterator;
- QByteArray key = QPasswordDigestor::deriveKeyPbkdf1(hashAlgorithm, passPhrase, salt, iterationCount, 16);
- if (key.size() != 16)
- return {};
-
- // Step 3
- QByteArray iv = key.right(8); // last 8 bytes are used as IV
- key.truncate(8); // first 8 bytes are used for the key
-
- QSslKeyPrivate::Cipher cipher = oidCipherMap[encryptionScheme];
-#ifdef Q_OS_WINRT
- // @todo: document this instead? find some other solution?
- if (cipher == QSslKeyPrivate::Cipher::Rc2Cbc)
- qWarning("PBES1 with RC2_CBC doesn't work properly on WinRT.");
-#endif
- // Steps 4-6 are done after returning
- return {cipher, key, iv};
-}
-
-QByteArray QSslKeyPrivate::decryptPkcs8(const QByteArray &encrypted, const QByteArray &passPhrase)
-{
- // RFC 5958: https://tools.ietf.org/html/rfc5958
- /*** Scheme: ***
- * Sequence
- * Sequence
- * Object Identifier (encryption scheme (currently PBES2, PBES1, @todo PKCS12))
- * Sequence (scheme parameters)
- * Octet String (the encrypted data)
- */
- QAsn1Element elem;
- if (!elem.read(encrypted) || elem.type() != QAsn1Element::SequenceType)
- return encrypted;
-
- const QVector<QAsn1Element> items = elem.toVector();
- if (items.size() != 2
- || items[0].type() != QAsn1Element::SequenceType
- || items[1].type() != QAsn1Element::OctetStringType) {
- return encrypted;
- }
-
- const QVector<QAsn1Element> encryptionSchemeContainer = items[0].toVector();
-
- if (encryptionSchemeContainer.size() != 2
- || encryptionSchemeContainer[0].type() != QAsn1Element::ObjectIdentifierType
- || encryptionSchemeContainer[1].type() != QAsn1Element::SequenceType) {
- return encrypted;
- }
-
- const QByteArray encryptionScheme = encryptionSchemeContainer[0].toObjectId();
- const QVector<QAsn1Element> schemeParameterContainer = encryptionSchemeContainer[1].toVector();
-
- if (schemeParameterContainer.size() != 2
- && schemeParameterContainer[0].type() != QAsn1Element::SequenceType
- && schemeParameterContainer[1].type() != QAsn1Element::SequenceType) {
- return encrypted;
- }
-
- EncryptionData data;
- if (encryptionScheme == PKCS5_PBES2_ENCRYPTION_OID) {
- data = readPbes2(schemeParameterContainer, passPhrase);
- } else if (pbes1OidHashFunctionMap.contains(encryptionScheme)) {
- data = readPbes1(schemeParameterContainer, encryptionScheme, passPhrase);
- } else if (encryptionScheme.startsWith(PKCS12_OID)) {
- Q_UNIMPLEMENTED(); // this isn't some 'unknown', I know these aren't implemented
- return encrypted;
- } else {
- qWarning()
- << "QSslKey: Unsupported encryption scheme OID:" << encryptionScheme
- << "\nFile a bugreport to Qt (include the line above).";
- return encrypted;
- }
-
- if (!data.initialized) {
- // something went wrong, return
- return encrypted;
- }
-
- QByteArray decryptedKey = decrypt(data.cipher, items[1].value(), data.key, data.iv);
- // The data is still wrapped in a octet string, so let's unwrap it
- QAsn1Element decryptedKeyElement(QAsn1Element::ElementType::OctetStringType, decryptedKey);
- return decryptedKeyElement.value();
-}
diff --git a/src/network/ssl/qsslkey_schannel.cpp b/src/network/ssl/qsslkey_schannel.cpp
deleted file mode 100644
index 1e21d123f4..0000000000
--- a/src/network/ssl/qsslkey_schannel.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qssl_p.h"
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qsslcertificate_p.h"
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qscopeguard.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
-{
- switch (cipher) {
- case QSslKeyPrivate::Cipher::DesCbc:
- return BCRYPT_DES_ALGORITHM;
- case QSslKeyPrivate::Cipher::DesEde3Cbc:
- return BCRYPT_3DES_ALGORITHM;
- case QSslKeyPrivate::Cipher::Rc2Cbc:
- return BCRYPT_RC2_ALGORITHM;
- case QSslKeyPrivate::Cipher::Aes128Cbc:
- case QSslKeyPrivate::Cipher::Aes192Cbc:
- case QSslKeyPrivate::Cipher::Aes256Cbc:
- return BCRYPT_AES_ALGORITHM;
- }
- Q_UNREACHABLE();
-}
-
-BCRYPT_ALG_HANDLE getHandle(QSslKeyPrivate::Cipher cipher)
-{
- BCRYPT_ALG_HANDLE handle;
- NTSTATUS status = BCryptOpenAlgorithmProvider(
- &handle, // phAlgorithm
- getName(cipher), // pszAlgId
- nullptr, // pszImplementation
- 0 // dwFlags
- );
- if (status < 0) {
- qCWarning(lcSsl, "Failed to open algorithm handle (%ld)!", status);
- return nullptr;
- }
-
- return handle;
-}
-
-BCRYPT_KEY_HANDLE generateSymmetricKey(BCRYPT_ALG_HANDLE handle,
- const QByteArray &key)
-{
- BCRYPT_KEY_HANDLE keyHandle;
- NTSTATUS status = BCryptGenerateSymmetricKey(
- handle, // hAlgorithm
- &keyHandle, // phKey
- nullptr, // pbKeyObject (can ignore)
- 0, // cbKeyObject (also ignoring)
- reinterpret_cast<unsigned char *>(const_cast<char *>(key.data())), // pbSecret
- ULONG(key.length()), // cbSecret
- 0 // dwFlags
- );
- if (status < 0) {
- qCWarning(lcSsl, "Failed to generate symmetric key (%ld)!", status);
- return nullptr;
- }
-
- status = BCryptSetProperty(
- keyHandle, // hObject
- BCRYPT_CHAINING_MODE, // pszProperty
- reinterpret_cast<UCHAR *>(const_cast<wchar_t *>(BCRYPT_CHAIN_MODE_CBC)), // pbInput
- ARRAYSIZE(BCRYPT_CHAIN_MODE_CBC), // cbInput
- 0 // dwFlags
- );
- if (status < 0) {
- BCryptDestroyKey(keyHandle);
- qCWarning(lcSsl, "Failed to change the symmetric key's chaining mode (%ld)!", status);
- return nullptr;
- }
- return keyHandle;
-}
-
-QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data, const QByteArray &key,
- const QByteArray &iv, bool encrypt)
-{
- BCRYPT_ALG_HANDLE handle = getHandle(cipher);
- if (!handle)
- return {};
- auto handleDealloc = qScopeGuard([&handle]() {
- BCryptCloseAlgorithmProvider(handle, 0);
- });
-
- BCRYPT_KEY_HANDLE keyHandle = generateSymmetricKey(handle, key);
- if (!keyHandle)
- return {};
- auto keyHandleDealloc = qScopeGuard([&keyHandle]() {
- BCryptDestroyKey(keyHandle);
- });
-
- QByteArray ivCopy = iv; // This gets modified, so we take a copy
-
- ULONG sizeNeeded = 0;
- QVarLengthArray<unsigned char> output;
- auto cryptFunction = encrypt ? BCryptEncrypt : BCryptDecrypt;
- for (int i = 0; i < 2; i++) {
- output.resize(int(sizeNeeded));
- auto input = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
- // Need to call it twice because the first iteration lets us know the size needed.
- NTSTATUS status = cryptFunction(
- keyHandle, // hKey
- input, // pbInput
- ULONG(data.length()), // cbInput
- nullptr, // pPaddingInfo
- reinterpret_cast<unsigned char *>(ivCopy.data()), // pbIV
- ULONG(ivCopy.length()), // cbIV
- sizeNeeded ? output.data() : nullptr, // pbOutput
- ULONG(output.length()), // cbOutput
- &sizeNeeded, // pcbResult
- BCRYPT_BLOCK_PADDING // dwFlags
- );
- if (status < 0) {
- qCWarning(lcSsl, "%s failed (%ld)!", encrypt ? "Encrypt" : "Decrypt", status);
- return {};
- }
- }
-
- return QByteArray(reinterpret_cast<const char *>(output.constData()), int(sizeNeeded));
-}
-} // anonymous namespace
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
- const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, false);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key,
- const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, true);
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp
deleted file mode 100644
index 69eaaa387f..0000000000
--- a/src/network/ssl/qsslkey_winrt.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qssl_p.h"
-#include "qsslkey.h"
-#include "qsslkey_p.h"
-#include "qsslcertificate_p.h"
-
-#include <QtCore/qfunctions_winrt.h>
-
-#include <wrl.h>
-#include <windows.security.cryptography.h>
-#include <windows.security.cryptography.core.h>
-#include <windows.security.cryptography.certificates.h>
-#include <windows.storage.streams.h>
-#include <robuffer.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Security::Cryptography;
-using namespace ABI::Windows::Security::Cryptography::Certificates;
-using namespace ABI::Windows::Security::Cryptography::Core;
-using namespace ABI::Windows::Storage::Streams;
-
-QT_USE_NAMESPACE
-
-struct SslKeyGlobal
-{
- SslKeyGlobal()
- {
- HRESULT hr;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_CryptographicEngine).Get(),
- &engine);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<ISymmetricKeyAlgorithmProviderStatics> keyProviderFactory;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_SymmetricKeyAlgorithmProvider).Get(),
- &keyProviderFactory);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"DES_CBC").Get(),
- &keyProviders[QSslKeyPrivate::DesCbc]);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"3DES_CBC").Get(),
- &keyProviders[QSslKeyPrivate::DesEde3Cbc]);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"RC2_CBC").Get(),
- &keyProviders[QSslKeyPrivate::Rc2Cbc]);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
- &keyProviders[QSslKeyPrivate::Aes128Cbc]);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
- &keyProviders[QSslKeyPrivate::Aes192Cbc]);
- Q_ASSERT_SUCCEEDED(hr);
- hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"AES_CBC").Get(),
- &keyProviders[QSslKeyPrivate::Aes256Cbc]);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(),
- &bufferFactory);
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- ComPtr<ICryptographicEngineStatics> engine;
- QHash<QSslKeyPrivate::Cipher, ComPtr<ISymmetricKeyAlgorithmProvider>> keyProviders;
- ComPtr<ICryptographicBufferStatics> bufferFactory;
-};
-Q_GLOBAL_STATIC(SslKeyGlobal, g)
-
-static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, QByteArray data, const QByteArray &key, const QByteArray &iv, bool encrypt)
-{
- HRESULT hr;
-
- ISymmetricKeyAlgorithmProvider *keyProvider = g->keyProviders[cipher].Get();
- Q_ASSERT(keyProvider);
-
- ComPtr<IBuffer> keyBuffer;
- hr = g->bufferFactory->CreateFromByteArray(key.length(), (BYTE *)key.data(), &keyBuffer);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<ICryptographicKey> cryptographicKey;
- hr = keyProvider->CreateSymmetricKey(keyBuffer.Get(), &cryptographicKey);
- Q_ASSERT_SUCCEEDED(hr);
-
- UINT32 blockLength;
- hr = keyProvider->get_BlockLength(&blockLength);
- Q_ASSERT_SUCCEEDED(hr);
- if (encrypt) { // Add padding
- const char padding = blockLength - data.length() % blockLength;
- data += QByteArray(padding, padding);
- }
-
- ComPtr<IBuffer> dataBuffer;
- hr = g->bufferFactory->CreateFromByteArray(data.length(), (BYTE *)data.data(), &dataBuffer);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IBuffer> ivBuffer;
- hr = g->bufferFactory->CreateFromByteArray(iv.length(), (BYTE *)iv.data(), &ivBuffer);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IBuffer> resultBuffer;
- hr = encrypt ? g->engine->Encrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer)
- : g->engine->Decrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer);
- Q_ASSERT_SUCCEEDED(hr);
-
- UINT32 resultLength;
- hr = resultBuffer->get_Length(&resultLength);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferAccess;
- hr = resultBuffer.As(&bufferAccess);
- Q_ASSERT_SUCCEEDED(hr);
- byte *resultData;
- hr = bufferAccess->Buffer(&resultData);
- Q_ASSERT_SUCCEEDED(hr);
-
- if (!encrypt) { // Remove padding
- const uchar padding = resultData[resultLength - 1];
- if (padding > 0 && padding <= blockLength)
- resultLength -= padding;
- else
- qCWarning(lcSsl, "Invalid padding length of %u; decryption likely failed.", padding);
- }
-
- return QByteArray(reinterpret_cast<const char *>(resultData), resultLength);
-}
-
-QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, false);
-}
-
-QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
-{
- return doCrypt(cipher, data, key, iv, true);
-}
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
index 01e1501763..0045a83bea 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsslpresharedkeyauthenticator.h"
#include "qsslpresharedkeyauthenticator_p.h"
@@ -44,6 +8,9 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QSslPreSharedKeyAuthenticator)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QSslPreSharedKeyAuthenticator*, QSslPreSharedKeyAuthenticator_ptr)
+
/*!
\internal
*/
@@ -141,7 +108,7 @@ QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(const QS
/*!
\fn QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(QSslPreSharedKeyAuthenticator &&authenticator)
- Move-assigns the the QSslPreSharedKeyAuthenticator object \a authenticator to this
+ Move-assigns the QSslPreSharedKeyAuthenticator object \a authenticator to this
object, and returns a reference to the moved instance.
*/
@@ -240,35 +207,36 @@ int QSslPreSharedKeyAuthenticator::maximumPreSharedKeyLength() const
}
/*!
- \relates QSslPreSharedKeyAuthenticator
+ \fn bool QSslPreSharedKeyAuthenticator::operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
\since 5.5
- Returns true if the authenticator object \a lhs is equal to \a rhs; false
- otherwise.
+ Returns \c true if the authenticator object \a lhs is equal to \a rhs;
+ \c false otherwise.
Two authenticator objects are equal if and only if they have the same
identity hint, identity, pre shared key, maximum length for the identity
and maximum length for the pre shared key.
-
*/
-bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
-{
- return ((lhs.d == rhs.d) ||
- (lhs.d->identityHint == rhs.d->identityHint &&
- lhs.d->identity == rhs.d->identity &&
- lhs.d->maximumIdentityLength == rhs.d->maximumIdentityLength &&
- lhs.d->preSharedKey == rhs.d->preSharedKey &&
- lhs.d->maximumPreSharedKeyLength == rhs.d->maximumPreSharedKeyLength));
-}
/*!
- \fn bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
- \relates QSslPreSharedKeyAuthenticator
+ \fn bool QSslPreSharedKeyAuthenticator::operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
\since 5.5
- Returns true if the authenticator object \a lhs is different than \a rhs;
- false otherwise.
+ Returns \c true if the authenticator object \a lhs is not equal to \a rhs;
+ \c false otherwise.
+*/
+/*!
+ \internal
*/
+bool QSslPreSharedKeyAuthenticator::isEqual(const QSslPreSharedKeyAuthenticator &other) const
+{
+ return ((d == other.d) ||
+ (d->identityHint == other.d->identityHint &&
+ d->identity == other.d->identity &&
+ d->maximumIdentityLength == other.d->maximumIdentityLength &&
+ d->preSharedKey == other.d->preSharedKey &&
+ d->maximumPreSharedKeyLength == other.d->maximumPreSharedKeyLength));
+}
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
index 5d714dc34e..a3912406d3 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLPRESHAREDKEYAUTHENTICATOR_H
#define QSSLPRESHAREDKEYAUTHENTICATOR_H
@@ -50,9 +14,9 @@ QT_REQUIRE_CONFIG(ssl);
QT_BEGIN_NAMESPACE
class QSslPreSharedKeyAuthenticatorPrivate;
-
class QSslPreSharedKeyAuthenticator
{
+ Q_GADGET_EXPORT(Q_NETWORK_EXPORT)
public:
Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator();
Q_NETWORK_EXPORT ~QSslPreSharedKeyAuthenticator();
@@ -61,7 +25,7 @@ public:
QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&other) noexcept { swap(other); return *this; }
- void swap(QSslPreSharedKeyAuthenticator &other) noexcept { qSwap(d, other.d); }
+ void swap(QSslPreSharedKeyAuthenticator &other) noexcept { d.swap(other.d); }
Q_NETWORK_EXPORT QByteArray identityHint() const;
@@ -74,23 +38,24 @@ public:
Q_NETWORK_EXPORT int maximumPreSharedKeyLength() const;
private:
- friend Q_NETWORK_EXPORT bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs);
- friend class QSslSocketBackendPrivate;
- friend class QDtlsPrivateOpenSSL;
+ Q_NETWORK_EXPORT bool isEqual(const QSslPreSharedKeyAuthenticator &other) const;
+
+ friend class QTlsBackend;
+
+ friend bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+ { return lhs.isEqual(rhs); }
+ friend bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+ { return !lhs.isEqual(rhs); }
QSharedDataPointer<QSslPreSharedKeyAuthenticatorPrivate> d;
};
-inline bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
-{
- return !operator==(lhs, rhs);
-}
Q_DECLARE_SHARED(QSslPreSharedKeyAuthenticator)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator)
-Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*)
+QT_DECL_METATYPE_EXTERN(QSslPreSharedKeyAuthenticator, Q_NETWORK_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QSslPreSharedKeyAuthenticator*, QSslPreSharedKeyAuthenticator_ptr, Q_NETWORK_EXPORT)
#endif // QSSLPRESHAREDKEYAUTHENTICATOR_H
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator_p.h b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
index e5566c3b3c..0075579074 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator_p.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 Governikus GmbH & Co. KG.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLPRESHAREDKEYAUTHENTICATOR_P_H
#define QSSLPRESHAREDKEYAUTHENTICATOR_P_H
diff --git a/src/network/ssl/qsslserver.cpp b/src/network/ssl/qsslserver.cpp
new file mode 100644
index 0000000000..40a6a6f526
--- /dev/null
+++ b/src/network/ssl/qsslserver.cpp
@@ -0,0 +1,412 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*!
+ \class QSslServer
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+ \since 6.4
+
+ \brief Implements an encrypted, secure TCP server over TLS.
+
+ Class to use in place of QTcpServer to implement TCP server using
+ Transport Layer Security (TLS).
+
+ To configure the secure handshake settings, use the applicable setter
+ functions on a QSslConfiguration object, and then use it as an argument
+ to the setSslConfiguration() function. All following incoming
+ connections handled will use these settings.
+
+ To start listening to incoming connections use the listen() function
+ inherited from QTcpServer. Other settings can be configured by using the
+ setter functions inherited from the QTcpServer class.
+
+ Connect to the signals of this class to respond to the incoming connection
+ attempts. They are the same as the signals on QSslSocket, but also
+ passes a pointer to the socket in question.
+
+ When responding to the pendingConnectionAvailable() signal, use the
+ nextPendingConnection() function to fetch the next incoming connection and
+ take it out of the pending connection queue. The QSslSocket is a child of
+ the QSslServer and will be deleted when the QSslServer is deleted. It is
+ still a good idea to destroy the object explicitly when you are done
+ with it, to avoid wasting memory.
+
+ \sa QTcpServer, QSslConfiguration, QSslSocket
+*/
+
+/*!
+ \fn void QSslServer::peerVerifyError(QSslSocket *socket, const QSslError &error)
+
+ QSslServer can emit this signal several times during the SSL handshake,
+ before encryption has been established, to indicate that an error has
+ occurred while establishing the identity of the peer. The \a error is
+ usually an indication that \a socket is unable to securely identify the
+ peer.
+
+ This signal provides you with an early indication when something's wrong.
+ By connecting to this signal, you can manually choose to tear down the
+ connection from inside the connected slot before the handshake has
+ completed. If no action is taken, QSslServer will proceed to emitting
+ sslErrors().
+
+ \sa sslErrors()
+*/
+
+/*!
+ \fn void QSslServer::sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
+
+ QSslServer emits this signal after the SSL handshake to indicate that one
+ or more errors have occurred while establishing the identity of the
+ peer. The errors are usually an indication that \a socket is unable to
+ securely identify the peer. Unless any action is taken, the connection
+ will be dropped after this signal has been emitted.
+
+ If you want to continue connecting despite the errors that have occurred,
+ you must call QSslSocket::ignoreSslErrors() from inside a slot connected to
+ this signal. If you need to access the error list at a later point, you
+ can call sslHandshakeErrors().
+
+ \a errors contains one or more errors that prevent QSslSocket from
+ verifying the identity of the peer.
+
+ \note You cannot use Qt::QueuedConnection when connecting to this signal,
+ or calling QSslSocket::ignoreSslErrors() will have no effect.
+
+ \sa peerVerifyError()
+*/
+
+/*!
+ \fn void QSslServer::errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError socketError)
+
+ This signal is emitted after an error occurred during handshake. The
+ \a socketError parameter describes the type of error that occurred.
+
+ The \a socket is automatically deleted after this signal is emitted if the
+ socket handshake has not reached encrypted state. But if the \a socket is
+ successfully encrypted, it is inserted into the QSslServer's pending
+ connections queue. When the user has called
+ QTcpServer::nextPendingConnection() it is the user's responsibility to
+ destroy the \a socket or the \a socket will not be destroyed until the
+ QSslServer object is destroyed. If an error occurs on a \a socket after
+ it has been inserted into the pending connections queue, this signal
+ will not be emitted, and the \a socket will not be removed or destroyed.
+
+ \note You cannot use Qt::QueuedConnection when connecting to this signal,
+ or the \a socket will have been already destroyed when the signal is
+ handled.
+
+ \sa QSslSocket::error(), errorString()
+*/
+
+/*!
+ \fn void QSslServer::preSharedKeyAuthenticationRequired(QSslSocket *socket,
+ QSslPreSharedKeyAuthenticator *authenticator)
+
+ QSslServer emits this signal when \a socket negotiates a PSK ciphersuite,
+ and therefore PSK authentication is then required.
+
+ When using PSK, the server must supply a valid identity and a valid pre
+ shared key, in order for the SSL handshake to continue.
+ Applications can provide this information in a slot connected to this
+ signal, by filling in the passed \a authenticator object according to their
+ needs.
+
+ \note Ignoring this signal, or failing to provide the required credentials,
+ will cause the handshake to fail, and therefore the connection to be aborted.
+
+ \note The \a authenticator object is owned by the \a socket and must not be
+ deleted by the application.
+
+ \sa QSslPreSharedKeyAuthenticator
+*/
+
+/*!
+ \fn void QSslServer::alertSent(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType type,
+ const QString &description)
+
+ QSslServer emits this signal if an alert message was sent from \a socket
+ to a peer. \a level describes if it was a warning or a fatal error.
+ \a type gives the code of the alert message. When a textual description
+ of the alert message is available, it is supplied in \a description.
+
+ \note This signal is mostly informational and can be used for debugging
+ purposes, normally it does not require any actions from the application.
+ \note Not all backends support this functionality.
+
+ \sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType
+*/
+
+/*!
+ \fn void QSslServer::alertReceived(QSslSocket *socket, QSsl::AlertLevel level, QSsl::AlertType
+ type, const QString &description)
+
+ QSslServer emits this signal if an alert message was received by the
+ \a socket from a peer. \a level tells if the alert was fatal or it was a
+ warning. \a type is the code explaining why the alert was sent.
+ When a textual description of the alert message is available, it is
+ supplied in \a description.
+
+ \note The signal is mostly for informational and debugging purposes and does not
+ require any handling in the application. If the alert was fatal, underlying
+ backend will handle it and close the connection.
+ \note Not all backends support this functionality.
+
+ \sa alertSent(), QSsl::AlertLevel, QSsl::AlertType
+*/
+
+/*!
+ \fn void QSslServer::handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error)
+
+ QSslServer emits this signal if a certificate verification error was found
+ by \a socket and if early error reporting was enabled in QSslConfiguration.
+ An application is expected to inspect the \a error and decide if it wants
+ to continue the handshake, or abort it and send an alert message to the
+ peer. The signal-slot connection must be direct.
+
+ \sa QSslSocket::continueInterruptedHandshake(), sslErrors(),
+ QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+
+/*!
+ \fn void QSslServer::startedEncryptionHandshake(QSslSocket *socket)
+
+ This signal is emitted when the client, connected to \a socket,
+ initiates the TLS handshake.
+*/
+
+#include "qsslserver.h"
+#include "qsslserver_p.h"
+
+#include <QtNetwork/QSslSocket>
+#include <QtNetwork/QSslCipher>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+*/
+QSslServerPrivate::QSslServerPrivate() :
+ sslConfiguration(QSslConfiguration::defaultConfiguration())
+{
+}
+
+/*!
+ Constructs a new QSslServer with the given \a parent.
+*/
+QSslServer::QSslServer(QObject *parent) :
+ QTcpServer(QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent)
+{
+}
+
+/*!
+ Destroys the QSslServer.
+
+ All open connections are closed.
+*/
+QSslServer::~QSslServer()
+{
+}
+
+/*!
+ Sets the \a sslConfiguration to use for all following incoming connections.
+
+ This must be called before listen() to ensure that the desired
+ configuration was in use during all handshakes.
+
+ \sa QSslSocket::setSslConfiguration()
+*/
+void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration)
+{
+ Q_D(QSslServer);
+ d->sslConfiguration = sslConfiguration;
+}
+
+/*!
+ Returns the current ssl configuration.
+*/
+QSslConfiguration QSslServer::sslConfiguration() const
+{
+ const Q_D(QSslServer);
+ return d->sslConfiguration;
+}
+
+/*!
+ Sets the \a timeout to use for all incoming handshakes, in milliseconds.
+
+ This is relevant in the scenario where a client, whether malicious or
+ accidental, connects to the server but makes no attempt at communicating or
+ initiating a handshake. QSslServer will then automatically end the
+ connection after \a timeout milliseconds have elapsed.
+
+ By default the timeout is 5000 milliseconds (5 seconds).
+
+ \note The underlying TLS framework may have their own timeout logic now or
+ in the future, this function does not affect that.
+
+ \note The \a timeout passed to this function will only apply to \e{new}
+ connections. If a client is already connected it will use the timeout which
+ was set when it connected.
+
+ \sa handshakeTimeout()
+*/
+void QSslServer::setHandshakeTimeout(int timeout)
+{
+ Q_D(QSslServer);
+ d->handshakeTimeout = timeout;
+}
+
+/*!
+ Returns the currently configured handshake timeout.
+
+ \sa setHandshakeTimeout()
+*/
+int QSslServer::handshakeTimeout() const
+{
+ const Q_D(QSslServer);
+ return d->handshakeTimeout;
+}
+
+/*!
+ Called when a new connection is established.
+
+ Converts \a socket to a QSslSocket.
+
+ \reimp
+*/
+void QSslServer::incomingConnection(qintptr socket)
+{
+ QSslSocket *pSslSocket = new QSslSocket(this);
+
+ pSslSocket->setSslConfiguration(sslConfiguration());
+
+ if (Q_LIKELY(pSslSocket->setSocketDescriptor(socket))) {
+ connect(pSslSocket, &QSslSocket::peerVerifyError, this,
+ [this, pSslSocket](const QSslError &error) {
+ Q_EMIT peerVerifyError(pSslSocket, error);
+ });
+ connect(pSslSocket, &QSslSocket::sslErrors, this,
+ [this, pSslSocket](const QList<QSslError> &errors) {
+ Q_EMIT sslErrors(pSslSocket, errors);
+ });
+ connect(pSslSocket, &QAbstractSocket::errorOccurred, this,
+ [this, pSslSocket](QAbstractSocket::SocketError error) {
+ Q_EMIT errorOccurred(pSslSocket, error);
+ if (!pSslSocket->isEncrypted())
+ pSslSocket->deleteLater();
+ });
+ connect(pSslSocket, &QSslSocket::encrypted, this, [this, pSslSocket]() {
+ Q_D(QSslServer);
+ d->removeSocketData(quintptr(pSslSocket));
+ pSslSocket->disconnect(this);
+ addPendingConnection(pSslSocket);
+ });
+ connect(pSslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, this,
+ [this, pSslSocket](QSslPreSharedKeyAuthenticator *authenticator) {
+ Q_EMIT preSharedKeyAuthenticationRequired(pSslSocket, authenticator);
+ });
+ connect(pSslSocket, &QSslSocket::alertSent, this,
+ [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type,
+ const QString &description) {
+ Q_EMIT alertSent(pSslSocket, level, type, description);
+ });
+ connect(pSslSocket, &QSslSocket::alertReceived, this,
+ [this, pSslSocket](QSsl::AlertLevel level, QSsl::AlertType type,
+ const QString &description) {
+ Q_EMIT alertReceived(pSslSocket, level, type, description);
+ });
+ connect(pSslSocket, &QSslSocket::handshakeInterruptedOnError, this,
+ [this, pSslSocket](const QSslError &error) {
+ Q_EMIT handshakeInterruptedOnError(pSslSocket, error);
+ });
+
+ d_func()->initializeHandshakeProcess(pSslSocket);
+ }
+}
+
+void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket)
+{
+ Q_Q(QSslServer);
+ QMetaObject::Connection readyRead = QObject::connect(
+ socket, &QSslSocket::readyRead, q, [this]() { checkClientHelloAndContinue(); });
+
+ QMetaObject::Connection destroyed =
+ QObject::connect(socket, &QSslSocket::destroyed, q, [this](QObject *obj) {
+ // This cast is not safe to use since the socket is inside the
+ // QObject dtor, but we only use the pointer value!
+ removeSocketData(quintptr(obj));
+ });
+ auto it = socketData.emplace(quintptr(socket), readyRead, destroyed, std::make_shared<QTimer>());
+ it->timeoutTimer->setSingleShot(true);
+ it->timeoutTimer->callOnTimeout(q, [this, socket]() { handleHandshakeTimedOut(socket); });
+ it->timeoutTimer->setInterval(handshakeTimeout);
+ it->timeoutTimer->start();
+}
+
+// This function may be called while in the socket's QObject dtor, __never__ use
+// the socket for anything other than a lookup!
+void QSslServerPrivate::removeSocketData(quintptr socket)
+{
+ auto it = socketData.find(socket);
+ if (it != socketData.end()) {
+ it->disconnectSignals();
+ socketData.erase(it);
+ }
+}
+
+int QSslServerPrivate::totalPendingConnections() const
+{
+ // max pending connections is int, so this cannot exceed that
+ return QTcpServerPrivate::totalPendingConnections() + int(socketData.size());
+}
+
+void QSslServerPrivate::checkClientHelloAndContinue()
+{
+ Q_Q(QSslServer);
+ QSslSocket *socket = qobject_cast<QSslSocket *>(q->sender());
+ if (Q_UNLIKELY(!socket) || socket->bytesAvailable() <= 0)
+ return;
+
+ char byte = '\0';
+ if (socket->peek(&byte, 1) != 1) {
+ socket->deleteLater();
+ return;
+ }
+
+ auto it = socketData.find(quintptr(socket));
+ const bool foundData = it != socketData.end();
+ if (foundData && it->readyReadConnection)
+ QObject::disconnect(std::exchange(it->readyReadConnection, {}));
+
+ constexpr char CLIENT_HELLO = 0x16;
+ if (byte != CLIENT_HELLO) {
+ socket->disconnectFromHost();
+ socket->deleteLater();
+ return;
+ }
+
+ // Be nice and restart the timeout timer since some progress was made
+ if (foundData)
+ it->timeoutTimer->start();
+
+ socket->startServerEncryption();
+ Q_EMIT q->startedEncryptionHandshake(socket);
+}
+
+void QSslServerPrivate::handleHandshakeTimedOut(QSslSocket *socket)
+{
+ Q_Q(QSslServer);
+ removeSocketData(quintptr(socket));
+ socket->disconnectFromHost();
+ Q_EMIT q->errorOccurred(socket, QAbstractSocket::SocketTimeoutError);
+ socket->deleteLater();
+ if (!socketEngine->isReadNotificationEnabled() && totalPendingConnections() < maxConnections)
+ q->resumeAccepting();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsslserver.cpp"
diff --git a/src/network/ssl/qsslserver.h b/src/network/ssl/qsslserver.h
new file mode 100644
index 0000000000..aaa0f43c35
--- /dev/null
+++ b/src/network/ssl/qsslserver.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSSLSERVER_H
+#define QSSLSERVER_H
+
+#include <QtNetwork/QTcpServer>
+
+QT_REQUIRE_CONFIG(ssl);
+
+#include <QtNetwork/QSslError>
+#include <QtNetwork/QSslConfiguration>
+#include <QtNetwork/QSslPreSharedKeyAuthenticator>
+#include <QtNetwork/QSslSocket>
+
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+
+class QSslSocket;
+class QSslServerPrivate;
+
+class Q_NETWORK_EXPORT QSslServer : public QTcpServer
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QSslServer)
+
+public:
+ explicit QSslServer(QObject *parent = nullptr);
+ ~QSslServer() override;
+
+ void setSslConfiguration(const QSslConfiguration &sslConfiguration);
+ QSslConfiguration sslConfiguration() const;
+
+ void setHandshakeTimeout(int timeout);
+ int handshakeTimeout() const;
+
+Q_SIGNALS:
+ void sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
+ void peerVerifyError(QSslSocket *socket, const QSslError &error);
+ void errorOccurred(QSslSocket *socket, QAbstractSocket::SocketError error);
+ void preSharedKeyAuthenticationRequired(QSslSocket *socket,
+ QSslPreSharedKeyAuthenticator *authenticator);
+ void alertSent(QSslSocket *socket, QSsl::AlertLevel level,
+ QSsl::AlertType type, const QString &description);
+ void alertReceived(QSslSocket *socket, QSsl::AlertLevel level,
+ QSsl::AlertType type, const QString &description);
+ void handshakeInterruptedOnError(QSslSocket *socket, const QSslError &error);
+ void startedEncryptionHandshake(QSslSocket *socket);
+
+protected:
+ void incomingConnection(qintptr socket) override;
+
+private:
+ Q_DECLARE_PRIVATE(QSslServer)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSSLSERVER_H
diff --git a/src/network/ssl/qsslserver_p.h b/src/network/ssl/qsslserver_p.h
new file mode 100644
index 0000000000..1b90d35d48
--- /dev/null
+++ b/src/network/ssl/qsslserver_p.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSSLSERVER_P_H
+#define QSSLSERVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qtimer.h>
+
+#include <QtNetwork/QSslConfiguration>
+#include <QtNetwork/private/qtcpserver_p.h>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class Q_NETWORK_EXPORT QSslServerPrivate : public QTcpServerPrivate
+{
+ static constexpr int DefaultHandshakeTimeout = 5'000; // 5 seconds
+public:
+ Q_DECLARE_PUBLIC(QSslServer)
+
+ QSslServerPrivate();
+ void checkClientHelloAndContinue();
+ void initializeHandshakeProcess(QSslSocket *socket);
+ void removeSocketData(quintptr socket);
+ void handleHandshakeTimedOut(QSslSocket *socket);
+ int totalPendingConnections() const override;
+
+ struct SocketData {
+ QMetaObject::Connection readyReadConnection;
+ QMetaObject::Connection destroyedConnection;
+ std::shared_ptr<QTimer> timeoutTimer; // shared_ptr because QHash demands copying
+
+ SocketData(QMetaObject::Connection readyRead, QMetaObject::Connection destroyed,
+ std::shared_ptr<QTimer> &&timer)
+ : readyReadConnection(readyRead),
+ destroyedConnection(destroyed),
+ timeoutTimer(std::move(timer))
+ {
+ }
+
+ void disconnectSignals()
+ {
+ QObject::disconnect(std::exchange(readyReadConnection, {}));
+ QObject::disconnect(std::exchange(destroyedConnection, {}));
+ }
+ };
+ QHash<quintptr, SocketData> socketData;
+
+ QSslConfiguration sslConfiguration;
+ int handshakeTimeout = DefaultHandshakeTimeout;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QSSLSERVER_P_H
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 690251727d..395394d432 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QSSLSOCKET_DEBUG
@@ -54,10 +18,10 @@
QSslSocket establishes a secure, encrypted TCP connection you can
use for transmitting encrypted data. It can operate in both client
- and server mode, and it supports modern SSL protocols, including
- SSL 3 and TLS 1.2. By default, QSslSocket uses only SSL protocols
+ and server mode, and it supports modern TLS protocols, including
+ TLS 1.3. By default, QSslSocket uses only TLS protocols
which are considered to be secure (QSsl::SecureProtocols), but you can
- change the SSL protocol by calling setProtocol() as long as you do
+ change the TLS protocol by calling setProtocol() as long as you do
it before the handshake has started.
SSL encryption operates on top of the existing TCP stream after
@@ -133,8 +97,7 @@
\list
\li The socket's cryptographic cipher suite can be customized before
- the handshake phase with QSslConfiguration::setCiphers()
- and QSslConfiguration::setDefaultCiphers().
+ the handshake phase with QSslConfiguration::setCiphers().
\li The socket's local certificate and private key can be customized
before the handshake phase with setLocalCertificate() and
setPrivateKey().
@@ -188,7 +151,7 @@
behavior is identical to QTcpSocket.
\value SslClientMode The socket is a client-side SSL socket.
- It is either alreayd encrypted, or it is in the SSL handshake
+ It is either already encrypted, or it is in the SSL handshake
phase (see QSslSocket::isEncrypted()).
\value SslServerMode The socket is a server-side SSL socket.
@@ -289,7 +252,7 @@
If you want to continue connecting despite the errors that have occurred,
you must call QSslSocket::ignoreSslErrors() from inside a slot connected to
this signal. If you need to access the error list at a later point, you
- can call sslErrors() (without arguments).
+ can call sslHandshakeErrors().
\a errors contains one or more errors that prevent QSslSocket from
verifying the identity of the peer.
@@ -322,23 +285,72 @@
\sa QSslPreSharedKeyAuthenticator
*/
+/*!
+ \fn void QSslSocket::alertSent(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description)
+
+ QSslSocket emits this signal if an alert message was sent to a peer. \a level
+ describes if it was a warning or a fatal error. \a type gives the code
+ of the alert message. When a textual description of the alert message is
+ available, it is supplied in \a description.
+
+ \note This signal is mostly informational and can be used for debugging
+ purposes, normally it does not require any actions from the application.
+ \note Not all backends support this functionality.
+
+ \sa alertReceived(), QSsl::AlertLevel, QSsl::AlertType
+*/
+
+/*!
+ \fn void QSslSocket::alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description)
+
+ QSslSocket emits this signal if an alert message was received from a peer.
+ \a level tells if the alert was fatal or it was a warning. \a type is the
+ code explaining why the alert was sent. When a textual description of
+ the alert message is available, it is supplied in \a description.
+
+ \note The signal is mostly for informational and debugging purposes and does not
+ require any handling in the application. If the alert was fatal, underlying
+ backend will handle it and close the connection.
+ \note Not all backends support this functionality.
+
+ \sa alertSent(), QSsl::AlertLevel, QSsl::AlertType
+*/
+
+/*!
+ \fn void QSslSocket::handshakeInterruptedOnError(const QSslError &error)
+
+ QSslSocket emits this signal if a certificate verification error was
+ found and if early error reporting was enabled in QSslConfiguration.
+ An application is expected to inspect the \a error and decide if
+ it wants to continue the handshake, or abort it and send an alert message
+ to the peer. The signal-slot connection must be direct.
+
+ \sa continueInterruptedHandshake(), sslErrors(), QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+
+/*!
+ \fn void QSslSocket::newSessionTicketReceived()
+ \since 5.15
+
+ If TLS 1.3 protocol was negotiated during a handshake, QSslSocket
+ emits this signal after receiving NewSessionTicket message. Session
+ and session ticket's lifetime hint are updated in the socket's
+ configuration. The session can be used for session resumption (and
+ a shortened handshake) in future TLS connections.
+
+ \note This functionality enabled only with OpenSSL backend and requires
+ OpenSSL v 1.1.1 or above.
+
+ \sa QSslSocket::sslConfiguration(), QSslConfiguration::sessionTicket(), QSslConfiguration::sessionTicketLifeTimeHint()
+*/
+
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslcipher.h"
#include "qocspresponse.h"
-#ifndef QT_NO_OPENSSL
-#include "qsslsocket_openssl_p.h"
-#endif
-#ifdef Q_OS_WINRT
-#include "qsslsocket_winrt_p.h"
-#endif
-#ifdef QT_SECURETRANSPORT
-#include "qsslsocket_mac_p.h"
-#endif
-#if QT_CONFIG(schannel)
-#include "qsslsocket_schannel_p.h"
-#endif
+#include "qtlsbackend_p.h"
#include "qsslconfiguration_p.h"
+#include "qsslsocket_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
@@ -350,6 +362,14 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+#ifdef Q_OS_VXWORKS
+constexpr auto isVxworks = true;
+#else
+constexpr auto isVxworks = false;
+#endif
+
class QSslSocketGlobalData
{
public:
@@ -364,7 +384,7 @@ public:
QMutex mutex;
QList<QSslCipher> supportedCiphers;
- QVector<QSslEllipticCurve> supportedEllipticCurves;
+ QList<QSslEllipticCurve> supportedEllipticCurves;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> config;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> dtlsConfig;
};
@@ -376,7 +396,7 @@ Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
set to the one returned by the static method defaultCiphers().
*/
QSslSocket::QSslSocket(QObject *parent)
- : QTcpSocket(*new QSslSocketBackendPrivate, parent)
+ : QTcpSocket(*new QSslSocketPrivate, parent)
{
Q_D(QSslSocket);
#ifdef QSSLSOCKET_DEBUG
@@ -842,10 +862,21 @@ void QSslSocket::close()
qCDebug(lcSsl) << "QSslSocket::close()";
#endif
Q_D(QSslSocket);
- if (encryptedBytesToWrite() || !d->writeBuffer.isEmpty())
+
+ // On Windows, CertGetCertificateChain is probably still doing its
+ // job, if the socket is re-used, we want to ignore its reported
+ // root CA.
+ if (auto *backend = d->backend.get())
+ backend->cancelCAFetch();
+
+ if (!d->abortCalled && (encryptedBytesToWrite() || !d->writeBuffer.isEmpty()))
flush();
- if (d->plainSocket)
- d->plainSocket->close();
+ if (d->plainSocket) {
+ if (d->abortCalled)
+ d->plainSocket->abort();
+ else
+ d->plainSocket->close();
+ }
QTcpSocket::close();
// must be cleared, reading/writing not possible on closed socket:
@@ -865,25 +896,6 @@ bool QSslSocket::atEnd() const
}
/*!
- This function writes as much as possible from the internal write buffer to
- the underlying network socket, without blocking. If any data was written,
- this function returns \c true; otherwise false is returned.
-
- Call this function if you need QSslSocket to start sending buffered data
- immediately. The number of bytes successfully written depends on the
- operating system. In most cases, you do not need to call this function,
- because QAbstractSocket will start sending data automatically once control
- goes back to the event loop. In the absence of an event loop, call
- waitForBytesWritten() instead.
-
- \sa write(), waitForBytesWritten()
-*/
-bool QSslSocket::flush()
-{
- return d_func()->flush();
-}
-
-/*!
\since 4.4
Sets the size of QSslSocket's internal read buffer to be \a size bytes.
@@ -898,24 +910,6 @@ void QSslSocket::setReadBufferSize(qint64 size)
}
/*!
- Aborts the current connection and resets the socket. Unlike
- disconnectFromHost(), this function immediately closes the socket,
- clearing any pending data in the write buffer.
-
- \sa disconnectFromHost(), close()
-*/
-void QSslSocket::abort()
-{
- Q_D(QSslSocket);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocket::abort()";
-#endif
- if (d->plainSocket)
- d->plainSocket->abort();
- close();
-}
-
-/*!
\since 4.4
Returns the socket's SSL configuration state. The default SSL
@@ -977,12 +971,17 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
#if QT_CONFIG(ocsp)
d->configuration.ocspStaplingEnabled = configuration.ocspStaplingEnabled();
#endif
-
+#if QT_CONFIG(openssl)
+ d->configuration.reportFromCallback = configuration.handshakeMustInterruptOnError();
+ d->configuration.missingCertIsFatal = configuration.missingCertificateIsFatal();
+#endif // openssl
// if the CA certificates were set explicitly (either via
// QSslConfiguration::setCaCertificates() or QSslSocket::setCaCertificates(),
// we cannot load the certificates on demand
- if (!configuration.d->allowRootCertOnDemandLoading)
+ if (!configuration.d->allowRootCertOnDemandLoading) {
d->allowRootCertOnDemandLoading = false;
+ d->configuration.allowRootCertOnDemandLoading = false;
+ }
}
/*!
@@ -1167,15 +1166,17 @@ QSsl::SslProtocol QSslSocket::sessionProtocol() const
\since 5.13
This function returns Online Certificate Status Protocol responses that
- a server may send during a TLS handshake using OCSP stapling. The vector
+ a server may send during a TLS handshake using OCSP stapling. The list
is empty if no definitive response or no response at all was received.
\sa QSslConfiguration::setOcspStaplingEnabled()
*/
-QVector<QOcspResponse> QSslSocket::ocspResponses() const
+QList<QOcspResponse> QSslSocket::ocspResponses() const
{
Q_D(const QSslSocket);
- return d->ocspResponses;
+ if (const auto *backend = d->backend.get())
+ return backend->ocsps();
+ return {};
}
/*!
@@ -1248,394 +1249,6 @@ QSslKey QSslSocket::privateKey() const
return d->configuration.privateKey;
}
-#if QT_DEPRECATED_SINCE(5, 5)
-/*!
- \deprecated
-
- Use QSslConfiguration::ciphers() instead.
-
- Returns this socket's current cryptographic cipher suite. This
- list is used during the socket's handshake phase for choosing a
- session cipher. The returned list of ciphers is ordered by
- descending preference. (i.e., the first cipher in the list is the
- most preferred cipher). The session cipher will be the first one
- in the list that is also supported by the peer.
-
- By default, the handshake phase can choose any of the ciphers
- supported by this system's SSL libraries, which may vary from
- system to system. The list of ciphers supported by this system's
- SSL libraries is returned by supportedCiphers(). You can restrict
- the list of ciphers used for choosing the session cipher for this
- socket by calling setCiphers() with a subset of the supported
- ciphers. You can revert to using the entire set by calling
- setCiphers() with the list returned by supportedCiphers().
-
- You can restrict the list of ciphers used for choosing the session
- cipher for \e all sockets by calling setDefaultCiphers() with a
- subset of the supported ciphers. You can revert to using the
- entire set by calling setCiphers() with the list returned by
- supportedCiphers().
-
- \sa setCiphers(), defaultCiphers(), setDefaultCiphers(), supportedCiphers()
-*/
-QList<QSslCipher> QSslSocket::ciphers() const
-{
- Q_D(const QSslSocket);
- return d->configuration.ciphers;
-}
-
-/*!
- \deprecated
-
- USe QSslConfiguration::setCiphers() instead.
-
- Sets the cryptographic cipher suite for this socket to \a ciphers,
- which must contain a subset of the ciphers in the list returned by
- supportedCiphers().
-
- Restricting the cipher suite must be done before the handshake
- phase, where the session cipher is chosen.
-
- \sa ciphers(), setDefaultCiphers(), supportedCiphers()
-*/
-void QSslSocket::setCiphers(const QList<QSslCipher> &ciphers)
-{
- Q_D(QSslSocket);
- d->configuration.ciphers = ciphers;
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::setCiphers() instead.
-
- Sets the cryptographic cipher suite for this socket to \a ciphers, which
- is a colon-separated list of cipher suite names. The ciphers are listed in
- order of preference, starting with the most preferred cipher. For example:
-
- \snippet code/src_network_ssl_qsslsocket.cpp 4
-
- Each cipher name in \a ciphers must be the name of a cipher in the
- list returned by supportedCiphers(). Restricting the cipher suite
- must be done before the handshake phase, where the session cipher
- is chosen.
-
- \sa ciphers(), setDefaultCiphers(), supportedCiphers()
-*/
-void QSslSocket::setCiphers(const QString &ciphers)
-{
- Q_D(QSslSocket);
- d->configuration.ciphers.clear();
- const auto cipherNames = ciphers.split(QLatin1Char(':'), QString::SkipEmptyParts);
- for (const QString &cipherName : cipherNames) {
- QSslCipher cipher(cipherName);
- if (!cipher.isNull())
- d->configuration.ciphers << cipher;
- }
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::setCiphers() on the default QSslConfiguration instead.
-
- Sets the default cryptographic cipher suite for all sockets in
- this application to \a ciphers, which must contain a subset of the
- ciphers in the list returned by supportedCiphers().
-
- Restricting the default cipher suite only affects SSL sockets
- that perform their handshake phase after the default cipher
- suite has been changed.
-
- \sa setCiphers(), defaultCiphers(), supportedCiphers()
-*/
-void QSslSocket::setDefaultCiphers(const QList<QSslCipher> &ciphers)
-{
- QSslSocketPrivate::setDefaultCiphers(ciphers);
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::ciphers() on the default QSslConfiguration instead.
-
- Returns the default cryptographic cipher suite for all sockets in
- this application. This list is used during the socket's handshake
- phase when negotiating with the peer to choose a session cipher.
- The list is ordered by preference (i.e., the first cipher in the
- list is the most preferred cipher).
-
- By default, the handshake phase can choose any of the ciphers
- supported by this system's SSL libraries, which may vary from
- system to system. The list of ciphers supported by this system's
- SSL libraries is returned by supportedCiphers().
-
- \sa supportedCiphers()
-*/
-QList<QSslCipher> QSslSocket::defaultCiphers()
-{
- return QSslSocketPrivate::defaultCiphers();
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::supportedCiphers() instead.
-
- Returns the list of cryptographic ciphers supported by this
- system. This list is set by the system's SSL libraries and may
- vary from system to system.
-
- \sa defaultCiphers(), ciphers(), setCiphers()
-*/
-QList<QSslCipher> QSslSocket::supportedCiphers()
-{
- return QSslSocketPrivate::supportedCiphers();
-}
-#endif // #if QT_DEPRECATED_SINCE(5, 5)
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificates() instead.
-
- Searches all files in the \a path for certificates encoded in the
- specified \a format and adds them to this socket's CA certificate
- database. \a path must be a file or a pattern matching one or more
- files, as specified by \a syntax. Returns \c true if one or more
- certificates are added to the socket's CA certificate database;
- otherwise returns \c false.
-
- The CA certificate database is used by the socket during the
- handshake phase to validate the peer's certificate.
-
- For more precise control, use addCaCertificate().
-
- \sa addCaCertificate(), QSslCertificate::fromPath()
-*/
-bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
-{
- Q_D(QSslSocket);
- QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
- if (certs.isEmpty())
- return false;
-
- d->configuration.caCertificates += certs;
- return true;
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificate() instead.
-
- Adds the \a certificate to this socket's CA certificate database.
- The CA certificate database is used by the socket during the
- handshake phase to validate the peer's certificate.
-
- To add multiple certificates, use addCaCertificates().
-
- \sa QSslConfiguration::caCertificates(),
- QSslConfiguration::setCaCertificates()
-*/
-void QSslSocket::addCaCertificate(const QSslCertificate &certificate)
-{
- Q_D(QSslSocket);
- d->configuration.caCertificates += certificate;
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificates() instead.
-
- Adds the \a certificates to this socket's CA certificate database.
- The CA certificate database is used by the socket during the
- handshake phase to validate the peer's certificate.
-
- For more precise control, use addCaCertificate().
-
- \sa QSslConfiguration::caCertificates(), addDefaultCaCertificate()
-*/
-void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates)
-{
- Q_D(QSslSocket);
- d->configuration.caCertificates += certificates;
-}
-
-#if QT_DEPRECATED_SINCE(5, 5)
-/*!
- \deprecated
-
- Use QSslConfiguration::setCaCertificates() instead.
-
- Sets this socket's CA certificate database to be \a certificates.
- The certificate database must be set prior to the SSL handshake.
- The CA certificate database is used by the socket during the
- handshake phase to validate the peer's certificate.
-
- The CA certificate database can be reset to the current default CA
- certificate database by calling this function with the list of CA
- certificates returned by defaultCaCertificates().
-
- \sa defaultCaCertificates()
-*/
-void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates)
-{
- Q_D(QSslSocket);
- d->configuration.caCertificates = certificates;
- d->allowRootCertOnDemandLoading = false;
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::caCertificates() instead.
-
- Returns this socket's CA certificate database. The CA certificate
- database is used by the socket during the handshake phase to
- validate the peer's certificate. It can be moodified prior to the
- handshake with addCaCertificate(), addCaCertificates(), and
- setCaCertificates().
-
- \note On Unix, this method may return an empty list if the root
- certificates are loaded on demand.
-
- \sa addCaCertificate(), addCaCertificates(), setCaCertificates()
-*/
-QList<QSslCertificate> QSslSocket::caCertificates() const
-{
- Q_D(const QSslSocket);
- return d->configuration.caCertificates;
-}
-#endif // #if QT_DEPRECATED_SINCE(5, 5)
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificates() on the default QSslConfiguration instead.
-
- Searches all files in the \a path for certificates with the
- specified \a encoding and adds them to the default CA certificate
- database. \a path can be an explicit file, or it can contain
- wildcards in the format specified by \a syntax. Returns \c true if
- any CA certificates are added to the default database.
-
- Each SSL socket's CA certificate database is initialized to the
- default CA certificate database.
-
- \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates(),
- QSslConfiguration::addDefaultCaCertificate()
-*/
-bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding,
- QRegExp::PatternSyntax syntax)
-{
- return QSslSocketPrivate::addDefaultCaCertificates(path, encoding, syntax);
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificate() on the default QSslConfiguration instead.
-
- Adds \a certificate to the default CA certificate database. Each
- SSL socket's CA certificate database is initialized to the default
- CA certificate database.
-
- \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates()
-*/
-void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
-{
- QSslSocketPrivate::addDefaultCaCertificate(certificate);
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::addCaCertificates() on the default QSslConfiguration instead.
-
- Adds \a certificates to the default CA certificate database. Each
- SSL socket's CA certificate database is initialized to the default
- CA certificate database.
-
- \sa QSslConfiguration::caCertificates(), QSslConfiguration::addCaCertificates()
-*/
-void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates)
-{
- QSslSocketPrivate::addDefaultCaCertificates(certificates);
-}
-
-#if QT_DEPRECATED_SINCE(5, 5)
-/*!
- \deprecated
-
- Use QSslConfiguration::setCaCertificates() on the default QSslConfiguration instead.
-
- Sets the default CA certificate database to \a certificates. The
- default CA certificate database is originally set to your system's
- default CA certificate database. You can override the default CA
- certificate database with your own CA certificate database using
- this function.
-
- Each SSL socket's CA certificate database is initialized to the
- default CA certificate database.
-
- \sa addDefaultCaCertificate()
-*/
-void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certificates)
-{
- QSslSocketPrivate::setDefaultCaCertificates(certificates);
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::caCertificates() on the default QSslConfiguration instead.
-
- Returns the current default CA certificate database. This database
- is originally set to your system's default CA certificate database.
- If no system default database is found, an empty database will be
- returned. You can override the default CA certificate database
- with your own CA certificate database using setDefaultCaCertificates().
-
- Each SSL socket's CA certificate database is initialized to the
- default CA certificate database.
-
- \note On Unix, this method may return an empty list if the root
- certificates are loaded on demand.
-
- \sa caCertificates()
-*/
-QList<QSslCertificate> QSslSocket::defaultCaCertificates()
-{
- return QSslSocketPrivate::defaultCaCertificates();
-}
-
-/*!
- \deprecated
-
- Use QSslConfiguration::systemDefaultCaCertificates instead.
-
- This function provides the CA certificate database
- provided by the operating system. The CA certificate database
- returned by this function is used to initialize the database
- returned by defaultCaCertificates(). You can replace that database
- with your own with setDefaultCaCertificates().
-
- \note: On OS X, only certificates that are either trusted for all
- purposes or trusted for the purpose of SSL in the keychain will be
- returned.
-
- \sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates()
-*/
-QList<QSslCertificate> QSslSocket::systemCaCertificates()
-{
- // we are calling ensureInitialized() in the method below
- return QSslSocketPrivate::systemCaCertificates();
-}
-#endif // #if QT_DEPRECATED_SINCE(5, 5)
-
/*!
Waits until the socket is connected, or \a msecs milliseconds,
whichever happens first. If the connection has been established,
@@ -1826,6 +1439,8 @@ bool QSslSocket::waitForDisconnected(int msecs)
}
/*!
+ \since 5.15
+
Returns a list of the last SSL errors that occurred. This is the
same list as QSslSocket passes via the sslErrors() signal. If the
connection has been encrypted with no errors, this function will
@@ -1833,10 +1448,12 @@ bool QSslSocket::waitForDisconnected(int msecs)
\sa connectToHostEncrypted()
*/
-QList<QSslError> QSslSocket::sslErrors() const
+QList<QSslError> QSslSocket::sslHandshakeErrors() const
{
Q_D(const QSslSocket);
- return d->sslErrors;
+ if (const auto *backend = d->backend.get())
+ return backend->tlsErrors();
+ return {};
}
/*!
@@ -1853,12 +1470,14 @@ bool QSslSocket::supportsSsl()
\since 5.0
Returns the version number of the SSL library in use. Note that
this is the version of the library in use at run-time not compile
- time. If no SSL support is available then this will return an
- undefined value.
+ time. If no SSL support is available then this will return -1.
*/
long QSslSocket::sslLibraryVersionNumber()
{
- return QSslSocketPrivate::sslLibraryVersionNumber();
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->tlsLibraryVersionNumber();
+
+ return -1;
}
/*!
@@ -1869,20 +1488,23 @@ long QSslSocket::sslLibraryVersionNumber()
*/
QString QSslSocket::sslLibraryVersionString()
{
- return QSslSocketPrivate::sslLibraryVersionString();
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->tlsLibraryVersionString();
+ return {};
}
/*!
\since 5.4
Returns the version number of the SSL library in use at compile
- time. If no SSL support is available then this will return an
- undefined value.
+ time. If no SSL support is available then this will return -1.
\sa sslLibraryVersionNumber()
*/
long QSslSocket::sslLibraryBuildVersionNumber()
{
- return QSslSocketPrivate::sslLibraryBuildVersionNumber();
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->tlsLibraryBuildVersionNumber();
+ return -1;
}
/*!
@@ -1895,7 +1517,166 @@ long QSslSocket::sslLibraryBuildVersionNumber()
*/
QString QSslSocket::sslLibraryBuildVersionString()
{
- return QSslSocketPrivate::sslLibraryBuildVersionString();
+ if (const auto *tlsBackend = QSslSocketPrivate::tlsBackendInUse())
+ return tlsBackend->tlsLibraryBuildVersionString();
+
+ return {};
+}
+
+/*!
+ \since 6.1
+ Returns the names of the currently available backends. These names
+ are in lower case, e.g. "openssl", "securetransport", "schannel"
+ (similar to the already existing feature names for TLS backends in Qt).
+
+ \sa activeBackend()
+*/
+QList<QString> QSslSocket::availableBackends()
+{
+ return QTlsBackend::availableBackendNames();
+}
+
+/*!
+ \since 6.1
+ Returns the name of the backend that QSslSocket and related classes
+ use. If the active backend was not set explicitly, this function
+ returns the name of a default backend that QSslSocket selects implicitly
+ from the list of available backends.
+
+ \note When selecting a default backend implicitly, QSslSocket prefers
+ the OpenSSL backend if available. If it's not available, the Schannel backend
+ is implicitly selected on Windows, and Secure Transport on Darwin platforms.
+ Failing these, if a custom TLS backend is found, it is used.
+ If no other backend is found, the "certificate only" backend is selected.
+ For more information about TLS plugins, please see
+ \l {Enabling and Disabling SSL Support when Building Qt from Source}.
+
+ \sa setActiveBackend(), availableBackends()
+*/
+QString QSslSocket::activeBackend()
+{
+ const QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+
+ if (!QSslSocketPrivate::activeBackendName.size())
+ QSslSocketPrivate::activeBackendName = QTlsBackend::defaultBackendName();
+
+ return QSslSocketPrivate::activeBackendName;
+}
+
+/*!
+ \since 6.1
+ Returns true if a backend with name \a backendName was set as
+ active backend. \a backendName must be one of names returned
+ by availableBackends().
+
+ \note An application cannot mix different backends simultaneously.
+ This implies that a non-default backend must be selected prior
+ to any use of QSslSocket or related classes, e.g. QSslCertificate
+ or QSslKey.
+
+ \sa activeBackend(), availableBackends()
+*/
+bool QSslSocket::setActiveBackend(const QString &backendName)
+{
+ if (!backendName.size()) {
+ qCWarning(lcSsl, "Invalid parameter (backend name cannot be an empty string)");
+ return false;
+ }
+
+ QMutexLocker locker(&QSslSocketPrivate::backendMutex);
+ if (QSslSocketPrivate::tlsBackend) {
+ qCWarning(lcSsl) << "Cannot set backend named" << backendName
+ << "as active, another backend is already in use";
+ locker.unlock();
+ return activeBackend() == backendName;
+ }
+
+ if (!QTlsBackend::availableBackendNames().contains(backendName)) {
+ qCWarning(lcSsl) << "Cannot set unavailable backend named" << backendName
+ << "as active";
+ return false;
+ }
+
+ QSslSocketPrivate::activeBackendName = backendName;
+
+ return true;
+}
+
+/*!
+ \since 6.1
+ If a backend with name \a backendName is available, this function returns the
+ list of TLS protocol versions supported by this backend. An empty \a backendName
+ is understood as a query about the currently active backend. Otherwise, this
+ function returns an empty list.
+
+ \sa availableBackends(), activeBackend(), isProtocolSupported()
+*/
+QList<QSsl::SslProtocol> QSslSocket::supportedProtocols(const QString &backendName)
+{
+ return QTlsBackend::supportedProtocols(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if \a protocol is supported by a backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa supportedProtocols()
+*/
+bool QSslSocket::isProtocolSupported(QSsl::SslProtocol protocol, const QString &backendName)
+{
+ const auto versions = supportedProtocols(backendName);
+ return versions.contains(protocol);
+}
+
+/*!
+ \since 6.1
+ This function returns backend-specific classes implemented by the backend named
+ \a backendName. An empty \a backendName is understood as a query about the
+ currently active backend.
+
+ \sa QSsl::ImplementedClass, activeBackend(), isClassImplemented()
+*/
+QList<QSsl::ImplementedClass> QSslSocket::implementedClasses(const QString &backendName)
+{
+ return QTlsBackend::implementedClasses(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if a class \a cl is implemented by the backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa implementedClasses()
+*/
+
+bool QSslSocket::isClassImplemented(QSsl::ImplementedClass cl, const QString &backendName)
+{
+ return implementedClasses(backendName).contains(cl);
+}
+
+/*!
+ \since 6.1
+ This function returns features supported by a backend named \a backendName.
+ An empty \a backendName is understood as a query about the currently active backend.
+
+ \sa QSsl::SupportedFeature, activeBackend()
+*/
+QList<QSsl::SupportedFeature> QSslSocket::supportedFeatures(const QString &backendName)
+{
+ return QTlsBackend::supportedFeatures(backendName.size() ? backendName : activeBackend());
+}
+
+/*!
+ \since 6.1
+ Returns true if a feature \a ft is supported by a backend named \a backendName. An empty
+ \a backendName is understood as a query about the currently active backend.
+
+ \sa QSsl::SupportedFeature, supportedFeatures()
+*/
+bool QSslSocket::isFeatureSupported(QSsl::SupportedFeature ft, const QString &backendName)
+{
+ return supportedFeatures(backendName).contains(ft);
}
/*!
@@ -2035,7 +1816,7 @@ void QSslSocket::ignoreSslErrors()
You can clear the list of errors you want to ignore by calling this
function with an empty list.
- \sa sslErrors()
+ \sa sslErrors(), sslHandshakeErrors()
*/
void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
{
@@ -2043,6 +1824,24 @@ void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
d->ignoreErrorsList = errors;
}
+
+/*!
+ \since 6.0
+
+ If an application wants to conclude a handshake even after receiving
+ handshakeInterruptedOnError() signal, it must call this function.
+ This call must be done from a slot function attached to the signal.
+ The signal-slot connection must be direct.
+
+ \sa handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+void QSslSocket::continueInterruptedHandshake()
+{
+ Q_D(QSslSocket);
+ if (auto *backend = d->backend.get())
+ backend->enableHandshakeContinuation();
+}
+
/*!
\internal
*/
@@ -2095,6 +1894,10 @@ void QSslSocket::disconnectFromHost()
d->pendingClose = true;
return;
}
+ // Make sure we don't process any signal from the CA fetcher
+ // (Windows):
+ if (auto *backend = d->backend.get())
+ backend->cancelCAFetch();
// Perhaps emit closing()
if (d->state != ClosingState) {
@@ -2130,7 +1933,7 @@ qint64 QSslSocket::readData(char *data, qint64 maxlen)
#endif
} else {
// possibly trigger another transmit() to decrypt more data from the socket
- if (d->plainSocket->bytesAvailable())
+ if (d->plainSocket->bytesAvailable() || d->hasUndecryptedData())
QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection);
else if (d->state != QAbstractSocket::ConnectedState)
return maxlen ? qint64(-1) : qint64(0);
@@ -2151,7 +1954,7 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
if (d->mode == UnencryptedMode && !d->autoStartHandshake)
return d->plainSocket->write(data, len);
- d->writeBuffer.append(data, len);
+ d->write(data, len);
// make sure we flush to the plain socket's buffer
if (!d->flushTriggered) {
@@ -2162,6 +1965,8 @@ qint64 QSslSocket::writeData(const char *data, qint64 len)
return len;
}
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
/*!
\internal
*/
@@ -2170,7 +1975,6 @@ QSslSocketPrivate::QSslSocketPrivate()
, mode(QSslSocket::UnencryptedMode)
, autoStartHandshake(false)
, connectionEncrypted(false)
- , shutdown(false)
, ignoreAllSslErrors(false)
, readyReadEmittedPointer(nullptr)
, allowRootCertOnDemandLoading(true)
@@ -2179,6 +1983,21 @@ QSslSocketPrivate::QSslSocketPrivate()
, flushTriggered(false)
{
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
+ // If the global configuration doesn't allow root certificates to be loaded
+ // on demand then we have to disable it for this socket as well.
+ if (!configuration.allowRootCertOnDemandLoading)
+ allowRootCertOnDemandLoading = false;
+
+ const auto *tlsBackend = tlsBackendInUse();
+ if (!tlsBackend) {
+ qCWarning(lcSsl, "No TLS backend is available");
+ return;
+ }
+ backend.reset(tlsBackend->createTlsCryptograph());
+ if (!backend.get()) {
+ qCWarning(lcSsl) << "The backend named" << tlsBackend->backendName()
+ << "does not support TLS";
+ }
}
/*!
@@ -2191,25 +2010,54 @@ QSslSocketPrivate::~QSslSocketPrivate()
/*!
\internal
*/
+bool QSslSocketPrivate::supportsSsl()
+{
+ if (const auto *tlsBackend = tlsBackendInUse())
+ return tlsBackend->implementedClasses().contains(QSsl::ImplementedClass::Socket);
+ return false;
+}
+
+/*!
+ \internal
+
+ Declared static in QSslSocketPrivate, makes sure the SSL libraries have
+ been initialized.
+*/
+void QSslSocketPrivate::ensureInitialized()
+{
+ if (!supportsSsl())
+ return;
+
+ const auto *tlsBackend = tlsBackendInUse();
+ Q_ASSERT(tlsBackend);
+ tlsBackend->ensureInitialized();
+}
+
+/*!
+ \internal
+*/
void QSslSocketPrivate::init()
{
+ // TLSTODO: delete those data members.
mode = QSslSocket::UnencryptedMode;
autoStartHandshake = false;
connectionEncrypted = false;
ignoreAllSslErrors = false;
- shutdown = false;
+ abortCalled = false;
pendingClose = false;
flushTriggered = false;
- ocspResponses.clear();
-
- // we don't want to clear the ignoreErrorsList, so
- // that it is possible setting it before connecting
-// ignoreErrorsList.clear();
+ // We don't want to clear the ignoreErrorsList, so
+ // that it is possible setting it before connecting.
buffer.clear();
writeBuffer.clear();
configuration.peerCertificate.clear();
configuration.peerCertificateChain.clear();
+
+ if (backend.get()) {
+ Q_ASSERT(q_ptr);
+ backend->init(static_cast<QSslSocket *>(q_ptr), this);
+ }
}
/*!
@@ -2217,13 +2065,27 @@ void QSslSocketPrivate::init()
*/
bool QSslSocketPrivate::verifyProtocolSupported(const char *where)
{
- if (configuration.protocol == QSsl::SslV2 || configuration.protocol == QSsl::SslV3) {
- qCWarning(lcSsl) << where << "Attempted to use an unsupported protocol.";
+ auto protocolName = "DTLS"_L1;
+ switch (configuration.protocol) {
+ case QSsl::UnknownProtocol:
+ // UnknownProtocol, according to our docs, is for cipher whose protocol is unknown.
+ // Should not be used when configuring QSslSocket.
+ protocolName = "UnknownProtocol"_L1;
+ Q_FALLTHROUGH();
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ case QSsl::DtlsV1_0:
+ case QSsl::DtlsV1_2:
+ case QSsl::DtlsV1_0OrLater:
+ case QSsl::DtlsV1_2OrLater:
+ qCWarning(lcSsl) << where << "QSslConfiguration with unexpected protocol" << protocolName;
setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
QSslSocket::tr("Attempted to use an unsupported protocol."));
return false;
+QT_WARNING_POP
+ default:
+ return true;
}
- return true;
}
/*!
@@ -2269,7 +2131,35 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
/*!
\internal
*/
-void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
+void QSslSocketPrivate::resetDefaultEllipticCurves()
+{
+ const auto *tlsBackend = tlsBackendInUse();
+ if (!tlsBackend)
+ return;
+
+ auto ids = tlsBackend->ellipticCurvesIds();
+ if (!ids.size())
+ return;
+
+ QList<QSslEllipticCurve> curves;
+ curves.reserve(ids.size());
+ for (int id : ids) {
+ QSslEllipticCurve curve;
+ curve.id = id;
+ curves.append(curve);
+ }
+
+ // Set the list of supported ECs, but not the list
+ // of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong
+ // ciphersuite, so don't try it -- leave the empty list to mean
+ // "the implementation will choose the most suitable one".
+ setDefaultSupportedEllipticCurves(curves);
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
{
QMutexLocker locker(&globalData()->mutex);
globalData()->dtlsConfig.detach();
@@ -2279,7 +2169,7 @@ void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
/*!
\internal
*/
-QList<QSslCipher> q_getDefaultDtlsCiphers()
+QList<QSslCipher> QSslSocketPrivate::defaultDtlsCiphers()
{
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
@@ -2289,7 +2179,7 @@ QList<QSslCipher> q_getDefaultDtlsCiphers()
/*!
\internal
*/
-QVector<QSslEllipticCurve> QSslSocketPrivate::supportedEllipticCurves()
+QList<QSslEllipticCurve> QSslSocketPrivate::supportedEllipticCurves()
{
QSslSocketPrivate::ensureInitialized();
const QMutexLocker locker(&globalData()->mutex);
@@ -2299,7 +2189,7 @@ QVector<QSslEllipticCurve> QSslSocketPrivate::supportedEllipticCurves()
/*!
\internal
*/
-void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVector<QSslEllipticCurve> &curves)
+void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QList<QSslEllipticCurve> &curves)
{
const QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
@@ -2336,29 +2226,12 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c
/*!
\internal
*/
-bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax)
-{
- QSslSocketPrivate::ensureInitialized();
- QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
- if (certs.isEmpty())
- return false;
-
- QMutexLocker locker(&globalData()->mutex);
- globalData()->config.detach();
- globalData()->config->caCertificates += certs;
- globalData()->dtlsConfig.detach();
- globalData()->dtlsConfig->caCertificates += certs;
- return true;
-}
-
-/*!
- \internal
-*/
void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert)
{
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
+ if (globalData()->config->caCertificates.contains(cert))
+ return;
globalData()->config.detach();
globalData()->config->caCertificates += cert;
globalData()->dtlsConfig.detach();
@@ -2422,6 +2295,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->sessionProtocol = global->sessionProtocol;
ptr->ciphers = global->ciphers;
ptr->caCertificates = global->caCertificates;
+ ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading;
ptr->protocol = global->protocol;
ptr->peerVerifyMode = global->peerVerifyMode;
ptr->peerVerifyDepth = global->peerVerifyDepth;
@@ -2434,6 +2308,10 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
#if QT_CONFIG(ocsp)
ptr->ocspStaplingEnabled = global->ocspStaplingEnabled;
#endif
+#if QT_CONFIG(openssl)
+ ptr->reportFromCallback = global->reportFromCallback;
+ ptr->missingCertIsFatal = global->missingCertIsFatal;
+#endif
}
/*!
@@ -2476,10 +2354,6 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->setPeerName(QString());
plainSocket = new QTcpSocket(q);
-#ifndef QT_NO_BEARERMANAGEMENT
- //copy network session down to the plain socket (if it has been set)
- plainSocket->setProperty("_q_networksession", q->property("_q_networksession"));
-#endif
q->connect(plainSocket, SIGNAL(connected()),
q, SLOT(_q_connectedSlot()),
Qt::DirectConnection);
@@ -2492,7 +2366,7 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->connect(plainSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
q, SLOT(_q_stateChangedSlot(QAbstractSocket::SocketState)),
Qt::DirectConnection);
- q->connect(plainSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ q->connect(plainSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
q, SLOT(_q_errorSlot(QAbstractSocket::SocketError)),
Qt::DirectConnection);
q->connect(plainSocket, SIGNAL(readyRead()),
@@ -2543,6 +2417,11 @@ bool QSslSocketPrivate::isPaused() const
return paused;
}
+void QSslSocketPrivate::setPaused(bool p)
+{
+ paused = p;
+}
+
bool QSslSocketPrivate::bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode)
{
// this function is called from QAbstractSocket::bind
@@ -2653,7 +2532,7 @@ void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state)
*/
void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
{
- Q_UNUSED(error)
+ Q_UNUSED(error);
#ifdef QSSLSOCKET_DEBUG
Q_Q(QSslSocket);
qCDebug(lcSsl) << "QSslSocket::_q_errorSlot(" << error << ')';
@@ -2661,7 +2540,7 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
qCDebug(lcSsl) << "\terrorString =" << q->errorString();
#endif
// this moves encrypted bytes from plain socket into our buffer
- if (plainSocket->bytesAvailable()) {
+ if (plainSocket->bytesAvailable() && mode != QSslSocket::UnencryptedMode) {
qint64 tmpReadBufferMaxSize = readBufferMaxSize;
readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
transmit();
@@ -2773,6 +2652,7 @@ void QSslSocketPrivate::_q_resumeImplementation()
if (verifyErrorsHaveBeenIgnored()) {
continueHandshake();
} else {
+ const auto sslErrors = backend->tlsErrors();
Q_ASSERT(!sslErrors.isEmpty());
setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
plainSocket->disconnectFromHost();
@@ -2787,13 +2667,16 @@ void QSslSocketPrivate::_q_resumeImplementation()
*/
bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
{
+ Q_ASSERT(backend.get());
+
bool doEmitSslError;
if (!ignoreErrorsList.empty()) {
// check whether the errors we got are all in the list of expected errors
// (applies only if the method QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
// was called)
+ const auto &sslErrors = backend->tlsErrors();
doEmitSslError = false;
- for (int a = 0; a < sslErrors.count(); a++) {
+ for (int a = 0; a < sslErrors.size(); a++) {
if (!ignoreErrorsList.contains(sslErrors.at(a))) {
doEmitSslError = true;
break;
@@ -2811,6 +2694,91 @@ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
/*!
\internal
*/
+bool QSslSocketPrivate::isAutoStartingHandshake() const
+{
+ return autoStartHandshake;
+}
+
+/*!
+ \internal
+*/
+bool QSslSocketPrivate::isPendingClose() const
+{
+ return pendingClose;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setPendingClose(bool pc)
+{
+ pendingClose = pc;
+}
+
+/*!
+ \internal
+*/
+qint64 QSslSocketPrivate::maxReadBufferSize() const
+{
+ return readBufferMaxSize;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setMaxReadBufferSize(qint64 maxSize)
+{
+ readBufferMaxSize = maxSize;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setEncrypted(bool enc)
+{
+ connectionEncrypted = enc;
+}
+
+/*!
+ \internal
+*/
+QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsWriteBuffer()
+{
+ return writeBuffer;
+}
+
+/*!
+ \internal
+*/
+QIODevicePrivate::QRingBufferRef &QSslSocketPrivate::tlsBuffer()
+{
+ return buffer;
+}
+
+/*!
+ \internal
+*/
+bool &QSslSocketPrivate::tlsEmittedBytesWritten()
+{
+ return emittedBytesWritten;
+}
+
+/*!
+ \internal
+*/
+bool *QSslSocketPrivate::readyReadPointer()
+{
+ return readyReadEmittedPointer;
+}
+
+bool QSslSocketPrivate::hasUndecryptedData() const
+{
+ return backend.get() && backend->hasUndecryptedData();
+}
+
+/*!
+ \internal
+*/
qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
{
if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) {
@@ -2826,9 +2794,9 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize)
if (r2 < 0)
return (r > 0 ? r : r2);
return r + r2;
- } else {
- return -1;
}
+
+ return -1;
} else {
//encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
return QTcpSocketPrivate::peek(data, maxSize);
@@ -2846,13 +2814,13 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
QByteArray ret;
ret.reserve(maxSize);
ret.resize(buffer.peek(ret.data(), maxSize, transactionPos));
- if (ret.length() == maxSize)
+ if (ret.size() == maxSize)
return ret;
//peek at data in the plain socket
if (plainSocket)
- return ret + plainSocket->peek(maxSize - ret.length());
- else
- return QByteArray();
+ return ret + plainSocket->peek(maxSize - ret.size());
+
+ return QByteArray();
} else {
//encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer
return QTcpSocketPrivate::peek(maxSize);
@@ -2860,17 +2828,19 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
}
/*!
- \internal
+ \reimp
*/
-qint64 QSslSocketPrivate::skip(qint64 maxSize)
+qint64 QSslSocket::skipData(qint64 maxSize)
{
- if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake)
- return plainSocket->skip(maxSize);
+ Q_D(QSslSocket);
+
+ if (d->mode == QSslSocket::UnencryptedMode && !d->autoStartHandshake)
+ return d->plainSocket->skip(maxSize);
// In encrypted mode, the SSL backend writes decrypted data directly into the
// QIODevice's read buffer. As this buffer is always emptied by the caller,
// we need to wait for more incoming data.
- return (state == QAbstractSocket::ConnectedState) ? Q_INT64_C(0) : Q_INT64_C(-1);
+ return (d->state == QAbstractSocket::ConnectedState) ? Q_INT64_C(0) : Q_INT64_C(-1);
}
/*!
@@ -2892,6 +2862,82 @@ bool QSslSocketPrivate::flush()
/*!
\internal
*/
+void QSslSocketPrivate::startClientEncryption()
+{
+ if (backend.get())
+ backend->startClientEncryption();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::startServerEncryption()
+{
+ if (backend.get())
+ backend->startServerEncryption();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::transmit()
+{
+ if (backend.get())
+ backend->transmit();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::disconnectFromHost()
+{
+ if (backend.get())
+ backend->disconnectFromHost();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::disconnected()
+{
+ if (backend.get())
+ backend->disconnected();
+}
+
+/*!
+ \internal
+*/
+QSslCipher QSslSocketPrivate::sessionCipher() const
+{
+ if (backend.get())
+ return backend->sessionCipher();
+
+ return {};
+}
+
+/*!
+ \internal
+*/
+QSsl::SslProtocol QSslSocketPrivate::sessionProtocol() const
+{
+ if (backend.get())
+ return backend->sessionProtocol();
+
+ return QSsl::UnknownProtocol;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::continueHandshake()
+{
+ if (backend.get())
+ backend->continueHandshake();
+}
+
+/*!
+ \internal
+*/
bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
{
return s_loadRootCertsOnDemand;
@@ -2900,34 +2946,63 @@ bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
/*!
\internal
*/
+void QSslSocketPrivate::setRootCertOnDemandLoadingSupported(bool supported)
+{
+ s_loadRootCertsOnDemand = supported;
+}
+
+/*!
+ \internal
+*/
QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
{
- return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva ...
- << "/usr/lib/ssl/certs/" // Gentoo, Mandrake
- << "/usr/share/ssl/" // Centos, Redhat, SuSE
- << "/usr/local/ssl/" // Normal OpenSSL Tarball
- << "/var/ssl/certs/" // AIX
- << "/usr/local/ssl/certs/" // Solaris
- << "/etc/openssl/certs/" // BlackBerry
- << "/opt/openssl/certs/" // HP-UX
- << "/etc/ssl/"; // OpenBSD
+ const auto ba = [](const auto &cstr) constexpr {
+ return QByteArray::fromRawData(std::begin(cstr), std::size(cstr) - 1);
+ };
+ static const QByteArray dirs[] = {
+ ba("/etc/ssl/certs/"), // (K)ubuntu, OpenSUSE, Mandriva ...
+ ba("/usr/lib/ssl/certs/"), // Gentoo, Mandrake
+ ba("/usr/share/ssl/"), // Centos, Redhat, SuSE
+ ba("/usr/local/ssl/"), // Normal OpenSSL Tarball
+ ba("/var/ssl/certs/"), // AIX
+ ba("/usr/local/ssl/certs/"), // Solaris
+ ba("/etc/openssl/certs/"), // BlackBerry
+ ba("/opt/openssl/certs/"), // HP-UX
+ ba("/etc/ssl/"), // OpenBSD
+ };
+ QList<QByteArray> result = QList<QByteArray>::fromReadOnlyData(dirs);
+ if constexpr (isVxworks) {
+ static QByteArray vxworksCertsDir = qgetenv("VXWORKS_CERTS_DIR");
+ if (!vxworksCertsDir.isEmpty())
+ result.push_back(vxworksCertsDir);
+ }
+ return result;
}
/*!
\internal
*/
-void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, QSharedPointer<QSslContext> sslContext)
+void QSslSocketPrivate::checkSettingSslContext(QSslSocket* socket, std::shared_ptr<QSslContext> tlsContext)
{
- if (socket->d_func()->sslContextPointer.isNull())
- socket->d_func()->sslContextPointer = sslContext;
+ if (!socket)
+ return;
+
+ if (auto *backend = socket->d_func()->backend.get())
+ backend->checkSettingSslContext(tlsContext);
}
/*!
\internal
*/
-QSharedPointer<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
+std::shared_ptr<QSslContext> QSslSocketPrivate::sslContext(QSslSocket *socket)
{
- return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer<QSslContext>();
+ if (!socket)
+ return {};
+
+ if (const auto *backend = socket->d_func()->backend.get())
+ return backend->sslContext();
+
+ return {};
}
bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName)
@@ -2967,17 +3042,17 @@ bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QS
*/
bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hostname)
{
- int wildcard = cn.indexOf(QLatin1Char('*'));
+ qsizetype wildcard = cn.indexOf(u'*');
// Check this is a wildcard cert, if not then just compare the strings
if (wildcard < 0)
- return QLatin1String(QUrl::toAce(cn)) == hostname;
+ return QLatin1StringView(QUrl::toAce(cn)) == hostname;
- int firstCnDot = cn.indexOf(QLatin1Char('.'));
- int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
+ qsizetype firstCnDot = cn.indexOf(u'.');
+ qsizetype secondCnDot = cn.indexOf(u'.', firstCnDot+1);
// Check at least 3 components
- if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
+ if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.size()))
return false;
// Check * is last character of 1st component (ie. there's a following .)
@@ -2985,22 +3060,22 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return false;
// Check only one star
- if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
+ if (cn.lastIndexOf(u'*') != wildcard)
return false;
// Reject wildcard character embedded within the A-labels or U-labels of an internationalized
// domain name (RFC6125 section 7.2)
- if (cn.startsWith(QLatin1String("xn--"), Qt::CaseInsensitive))
+ if (cn.startsWith("xn--"_L1, Qt::CaseInsensitive))
return false;
// Check characters preceding * (if any) match
- if (wildcard && hostname.leftRef(wildcard).compare(cn.leftRef(wildcard), Qt::CaseInsensitive) != 0)
+ if (wildcard && QStringView{hostname}.left(wildcard).compare(QStringView{cn}.left(wildcard), Qt::CaseInsensitive) != 0)
return false;
// Check characters following first . match
- int hnDot = hostname.indexOf(QLatin1Char('.'));
- if (hostname.midRef(hnDot + 1) != cn.midRef(firstCnDot + 1)
- && hostname.midRef(hnDot + 1) != QLatin1String(QUrl::toAce(cn.mid(firstCnDot + 1)))) {
+ qsizetype hnDot = hostname.indexOf(u'.');
+ if (QStringView{hostname}.mid(hnDot + 1) != QStringView{cn}.mid(firstCnDot + 1)
+ && QStringView{hostname}.mid(hnDot + 1) != QLatin1StringView(QUrl::toAce(cn.mid(firstCnDot + 1)))) {
return false;
}
@@ -3013,6 +3088,81 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
return true;
}
+/*!
+ \internal
+*/
+QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
+{
+ const QMutexLocker locker(&backendMutex);
+ if (tlsBackend)
+ return tlsBackend;
+
+ if (!activeBackendName.size())
+ activeBackendName = QTlsBackend::defaultBackendName();
+
+ if (!activeBackendName.size()) {
+ qCWarning(lcSsl, "No functional TLS backend was found");
+ return nullptr;
+ }
+
+ tlsBackend = QTlsBackend::findBackend(activeBackendName);
+ if (tlsBackend) {
+ QObject::connect(tlsBackend, &QObject::destroyed, tlsBackend, [] {
+ const QMutexLocker locker(&backendMutex);
+ tlsBackend = nullptr;
+ },
+ Qt::DirectConnection);
+ }
+ return tlsBackend;
+}
+
+/*!
+ \internal
+*/
+QSslSocket::SslMode QSslSocketPrivate::tlsMode() const
+{
+ return mode;
+}
+
+/*!
+ \internal
+*/
+bool QSslSocketPrivate::isRootsOnDemandAllowed() const
+{
+ return allowRootCertOnDemandLoading;
+}
+
+/*!
+ \internal
+*/
+QString QSslSocketPrivate::verificationName() const
+{
+ return verificationPeerName;
+}
+
+/*!
+ \internal
+*/
+QString QSslSocketPrivate::tlsHostName() const
+{
+ return hostName;
+}
+
+QTcpSocket *QSslSocketPrivate::plainTcpSocket() const
+{
+ return plainSocket;
+}
+
+/*!
+ \internal
+*/
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ if (const auto *tlsBackend = tlsBackendInUse())
+ return tlsBackend->systemCaCertificates();
+ return {};
+}
+
QT_END_NAMESPACE
#include "moc_qsslsocket.cpp"
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 843e2d15f5..3ed1bc45cc 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLSOCKET_H
@@ -43,8 +7,6 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
-#include <QtCore/qregexp.h>
-#include <QtCore/qvector.h>
#ifndef QT_NO_SSL
# include <QtNetwork/qtcpsocket.h>
# include <QtNetwork/qsslerror.h>
@@ -59,7 +21,6 @@ class QDir;
class QSslCipher;
class QSslCertificate;
class QSslConfiguration;
-class QSslEllipticCurve;
class QSslPreSharedKeyAuthenticator;
class QOcspResponse;
@@ -67,12 +28,14 @@ class QSslSocketPrivate;
class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
{
Q_OBJECT
+ Q_MOC_INCLUDE(<QtNetwork/qsslpresharedkeyauthenticator.h>)
public:
enum SslMode {
UnencryptedMode,
SslClientMode,
SslServerMode
};
+ Q_ENUM(SslMode)
enum PeerVerifyMode {
VerifyNone,
@@ -80,6 +43,7 @@ public:
VerifyPeer,
AutoVerifyPeer
};
+ Q_ENUM(PeerVerifyMode)
explicit QSslSocket(QObject *parent = nullptr);
~QSslSocket();
@@ -119,8 +83,6 @@ public:
bool canReadLine() const override;
void close() override;
bool atEnd() const override;
- bool flush(); // ### Qt6: remove me (implementation moved to private flush())
- void abort();
// From QAbstractSocket:
void setReadBufferSize(qint64 size) override;
@@ -144,7 +106,7 @@ public:
QList<QSslCertificate> peerCertificateChain() const;
QSslCipher sessionCipher() const;
QSsl::SslProtocol sessionProtocol() const;
- QVector<QOcspResponse> ocspResponses() const;
+ QList<QOcspResponse> ocspResponses() const;
// Private keys, for server sockets.
void setPrivateKey(const QSslKey &key);
@@ -153,46 +115,13 @@ public:
const QByteArray &passPhrase = QByteArray());
QSslKey privateKey() const;
- // Cipher settings.
-#if QT_DEPRECATED_SINCE(5, 5)
- QT_DEPRECATED_X("Use QSslConfiguration::ciphers()") QList<QSslCipher> ciphers() const;
- QT_DEPRECATED_X("Use QSslConfiguration::setCiphers()") void setCiphers(const QList<QSslCipher> &ciphers);
- QT_DEPRECATED void setCiphers(const QString &ciphers);
- QT_DEPRECATED static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
- QT_DEPRECATED static QList<QSslCipher> defaultCiphers();
- QT_DEPRECATED_X("Use QSslConfiguration::supportedCiphers()") static QList<QSslCipher> supportedCiphers();
-#endif // QT_DEPRECATED_SINCE(5, 5)
-
- // CA settings.
-#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
- QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificate()") void addCaCertificate(const QSslCertificate &certificate);
- QT_DEPRECATED_X("Use QSslConfiguration::addCaCertificates()") void addCaCertificates(const QList<QSslCertificate> &certificates);
-#endif // QT_DEPRECATED_SINCE(5, 15)
-#if QT_DEPRECATED_SINCE(5, 5)
- QT_DEPRECATED_X("Use QSslConfiguration::setCaCertificates()") void setCaCertificates(const QList<QSslCertificate> &certificates);
- QT_DEPRECATED_X("Use QSslConfiguration::caCertificates()") QList<QSslCertificate> caCertificates() const;
-#endif // QT_DEPRECATED_SINCE(5, 5)
-#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
- QRegExp::PatternSyntax syntax = QRegExp::FixedString);
- QT_DEPRECATED static void addDefaultCaCertificate(const QSslCertificate &certificate);
- QT_DEPRECATED static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
-#endif // QT_DEPRECATED_SINCE(5, 15)
-#if QT_DEPRECATED_SINCE(5, 5)
- QT_DEPRECATED static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates);
- QT_DEPRECATED static QList<QSslCertificate> defaultCaCertificates();
- QT_DEPRECATED_X("Use QSslConfiguration::systemCaCertificates()") static QList<QSslCertificate> systemCaCertificates();
-#endif // QT_DEPRECATED_SINCE(5, 5)
-
bool waitForConnected(int msecs = 30000) override;
bool waitForEncrypted(int msecs = 30000);
bool waitForReadyRead(int msecs = 30000) override;
bool waitForBytesWritten(int msecs = 30000) override;
bool waitForDisconnected(int msecs = 30000) override;
- QList<QSslError> sslErrors() const;
+ QList<QSslError> sslHandshakeErrors() const;
static bool supportsSsl();
static long sslLibraryVersionNumber();
@@ -200,7 +129,18 @@ public:
static long sslLibraryBuildVersionNumber();
static QString sslLibraryBuildVersionString();
+ static QList<QString> availableBackends();
+ static QString activeBackend();
+ static bool setActiveBackend(const QString &backendName);
+ static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName = {});
+ static bool isProtocolSupported(QSsl::SslProtocol protocol, const QString &backendName = {});
+ static QList<QSsl::ImplementedClass> implementedClasses(const QString &backendName = {});
+ static bool isClassImplemented(QSsl::ImplementedClass cl, const QString &backendName = {});
+ static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName = {});
+ static bool isFeatureSupported(QSsl::SupportedFeature feat, const QString &backendName = {});
+
void ignoreSslErrors(const QList<QSslError> &errors);
+ void continueInterruptedHandshake();
public Q_SLOTS:
void startClientEncryption();
@@ -214,14 +154,20 @@ Q_SIGNALS:
void modeChanged(QSslSocket::SslMode newMode);
void encryptedBytesWritten(qint64 totalBytes);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
+ void newSessionTicketReceived();
+ void alertSent(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description);
+ void alertReceived(QSsl::AlertLevel level, QSsl::AlertType type, const QString &description);
+ void handshakeInterruptedOnError(const QSslError &error);
protected:
qint64 readData(char *data, qint64 maxlen) override;
+ qint64 skipData(qint64 maxSize) override;
qint64 writeData(const char *data, qint64 len) override;
private:
Q_DECLARE_PRIVATE(QSslSocket)
- Q_DISABLE_COPY(QSslSocket)
+ Q_DISABLE_COPY_MOVE(QSslSocket)
+
Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot())
Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot())
Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot())
@@ -235,10 +181,6 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer())
Q_PRIVATE_SLOT(d_func(), void _q_resumeImplementation())
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
- Q_PRIVATE_SLOT(d_func(), void _q_caRootLoaded(QSslCertificate,QSslCertificate))
-#endif
- friend class QSslSocketBackendPrivate;
};
#endif // QT_NO_SSL
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
deleted file mode 100644
index e0e065679d..0000000000
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ /dev/null
@@ -1,1510 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsslsocket.h"
-
-#include "qssl_p.h"
-#include "qsslsocket_mac_p.h"
-#include "qasn1element_p.h"
-#include "qsslcertificate_p.h"
-#include "qsslcipher_p.h"
-#include "qsslkey_p.h"
-
-#include <QtCore/qmessageauthenticationcode.h>
-#include <QtCore/qoperatingsystemversion.h>
-#include <QtCore/qscopedvaluerollback.h>
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qsystemdetection.h>
-#include <QtCore/qdatastream.h>
-#include <QtCore/qsysinfo.h>
-#include <QtCore/qvector.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/quuid.h>
-#include <QtCore/qdir.h>
-
-#include <algorithm>
-#include <cstddef>
-#include <limits>
-#include <vector>
-
-#include <QtCore/private/qcore_mac_p.h>
-
-#ifdef Q_OS_OSX
-#include <CoreServices/CoreServices.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace
-{
-#ifdef Q_OS_MACOS
-/*
-
-Our own temporarykeychain is needed only on macOS where SecPKCS12Import changes
-the default keychain and where we see annoying pop-ups asking about accessing a
-private key.
-
-*/
-
-struct EphemeralSecKeychain
-{
- EphemeralSecKeychain();
- ~EphemeralSecKeychain();
-
- SecKeychainRef keychain = nullptr;
- Q_DISABLE_COPY_MOVE(EphemeralSecKeychain)
-};
-
-EphemeralSecKeychain::EphemeralSecKeychain()
-{
- const auto uuid = QUuid::createUuid();
- if (uuid.isNull()) {
- qCWarning(lcSsl) << "Failed to create a unique keychain name";
- return;
- }
-
- const QByteArray uuidAsByteArray = uuid.toByteArray();
- Q_ASSERT(uuidAsByteArray.size() > 2);
- Q_ASSERT(uuidAsByteArray.startsWith('{'));
- Q_ASSERT(uuidAsByteArray.endsWith('}'));
- const auto uuidAsString = QLatin1String(uuidAsByteArray.data(), uuidAsByteArray.size()).mid(1, uuidAsByteArray.size() - 2);
-
- const QString keychainName
- = QDir::tempPath() + QDir::separator() + uuidAsString + QLatin1String(".keychain");
- // SecKeychainCreate, pathName parameter:
- //
- // "A constant character string representing the POSIX path indicating where
- // to store the keychain."
- //
- // Internally they seem to use std::string, but this does not really help.
- // Fortunately, CFString has a convenient API.
- QCFType<CFStringRef> cfName = keychainName.toCFString();
- std::vector<char> posixPath;
- // "Extracts the contents of a string as a NULL-terminated 8-bit string
- // appropriate for passing to POSIX APIs."
- posixPath.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfName));
- const auto ok = CFStringGetFileSystemRepresentation(cfName, &posixPath[0],
- CFIndex(posixPath.size()));
- if (!ok) {
- qCWarning(lcSsl) << "Failed to create a unique keychain name from"
- << "QDir::tempPath()";
- return;
- }
-
- std::vector<uint8_t> passUtf8(256);
- if (SecRandomCopyBytes(kSecRandomDefault, passUtf8.size(), &passUtf8[0])) {
- qCWarning(lcSsl) << "SecRandomCopyBytes: failed to create a key";
- return;
- }
-
- const OSStatus status = SecKeychainCreate(&posixPath[0], passUtf8.size(),
- &passUtf8[0], FALSE, nullptr,
- &keychain);
- if (status != errSecSuccess || !keychain) {
- qCWarning(lcSsl) << "SecKeychainCreate: failed to create a custom keychain";
- if (keychain) {
- SecKeychainDelete(keychain);
- CFRelease(keychain);
- keychain = nullptr;
- }
- }
-
- if (keychain) {
- SecKeychainSettings settings = {};
- settings.version = SEC_KEYCHAIN_SETTINGS_VERS1;
- // Strange, huh? But that's what their docs say to do! With lockOnSleep
- // == false, set interval to INT_MAX to never lock ...
- settings.lockInterval = INT_MAX;
- if (SecKeychainSetSettings(keychain, &settings) != errSecSuccess)
- qCWarning(lcSsl) << "SecKeychainSettings: failed to disable lock on sleep";
- }
-
-#ifdef QSSLSOCKET_DEBUG
- if (keychain) {
- qCDebug(lcSsl) << "Custom keychain with name" << keychainName << "was created"
- << "successfully";
- }
-#endif
-}
-
-EphemeralSecKeychain::~EphemeralSecKeychain()
-{
- if (keychain) {
- // clear file off disk
- SecKeychainDelete(keychain);
- CFRelease(keychain);
- }
-}
-
-#endif // Q_OS_MACOS
-
-} // unnamed namespace
-
-static SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode)
-{
- const bool isServer = mode == QSslSocket::SslServerMode;
- const SSLProtocolSide side = isServer ? kSSLServerSide : kSSLClientSide;
- // We never use kSSLDatagramType, so it's kSSLStreamType unconditionally.
- SSLContextRef context = SSLCreateContext(nullptr, side, kSSLStreamType);
- if (!context)
- qCWarning(lcSsl) << "SSLCreateContext failed";
- return context;
-}
-
-static void qt_releaseSecureTransportContext(SSLContextRef context)
-{
- if (context)
- CFRelease(context);
-}
-
-QSecureTransportContext::QSecureTransportContext(SSLContextRef c)
- : context(c)
-{
-}
-
-QSecureTransportContext::~QSecureTransportContext()
-{
- qt_releaseSecureTransportContext(context);
-}
-
-QSecureTransportContext::operator SSLContextRef()const
-{
- return context;
-}
-
-void QSecureTransportContext::reset(SSLContextRef newContext)
-{
- qt_releaseSecureTransportContext(context);
- context = newContext;
-}
-
-Q_GLOBAL_STATIC(QRecursiveMutex, qt_securetransport_mutex)
-
-//#define QSSLSOCKET_DEBUG
-
-bool QSslSocketPrivate::s_libraryLoaded = false;
-bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
-bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
-
-
-#if !defined(QT_PLATFORM_UIKIT) // dhparam is only used on macOS. (see the SSLSetDiffieHellmanParams call below)
-static const uint8_t dhparam[] =
- "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
- "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
- "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4"
- "\x0e\x46\xcf\xe5\x80\xdf\xc9\xb9\xba\x54\x9b\x46\x2f\x3b\x45\xfc\x2f\xaf"
- "\xad\xc0\x17\x56\xdd\x52\x42\x57\x45\x70\x14\xe5\xbe\x67\xaa\xde\x69\x75"
- "\x30\x0d\xf9\xa2\xc4\x63\x4d\x7a\x39\xef\x14\x62\x18\x33\x44\xa1\xf9\xc1"
- "\x52\xd1\xb6\x72\x21\x98\xf8\xab\x16\x1b\x7b\x37\x65\xe3\xc5\x11\x00\xf6"
- "\x36\x1f\xd8\x5f\xd8\x9f\x43\xa8\xce\x9d\xbf\x5e\xd6\x2d\xfa\x0a\xc2\x01"
- "\x54\xc2\xd9\x81\x54\x55\xb5\x26\xf8\x88\x37\xf5\xfe\xe0\xef\x4a\x34\x81"
- "\xdc\x5a\xb3\x71\x46\x27\xe3\xcd\x24\xf6\x1b\xf1\xe2\x0f\xc2\xa1\x39\x53"
- "\x5b\xc5\x38\x46\x8e\x67\x4c\xd9\xdd\xe4\x37\x06\x03\x16\xf1\x1d\x7a\xba"
- "\x2d\xc1\xe4\x03\x1a\x58\xe5\x29\x5a\x29\x06\x69\x61\x7a\xd8\xa9\x05\x9f"
- "\xc1\xa2\x45\x9c\x17\xad\x52\x69\x33\xdc\x18\x8d\x15\xa6\x5e\xcd\x94\xf4"
- "\x45\xbb\x9f\xc2\x7b\x85\x00\x61\xb0\x1a\xdc\x3c\x86\xaa\x9f\x5c\x04\xb3"
- "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
-#endif
-
-OSStatus QSslSocketBackendPrivate::ReadCallback(QSslSocketBackendPrivate *socket,
- char *data, size_t *dataLength)
-{
- Q_ASSERT(socket);
- Q_ASSERT(data);
- Q_ASSERT(dataLength);
-
- QTcpSocket *plainSocket = socket->plainSocket;
- Q_ASSERT(plainSocket);
-
- if (socket->isHandshakeComplete()) {
- // Check if it's a renegotiation attempt, when the handshake is complete, the
- // session state is 'kSSLConnected':
- SSLSessionState currentState = kSSLConnected;
- const OSStatus result = SSLGetSessionState(socket->context, &currentState);
- if (result != noErr) {
- *dataLength = 0;
- return result;
- }
-
- if (currentState == kSSLHandshake) {
- // Renegotiation detected, don't allow read more yet - 'transmit'
- // will notice this and will call 'startHandshake':
- *dataLength = 0;
- socket->renegotiating = true;
- return errSSLWouldBlock;
- }
- }
-
- const qint64 bytes = plainSocket->read(data, *dataLength);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "read" << bytes;
-#endif
- if (bytes < 0) {
- *dataLength = 0;
- return errSecIO;
- }
-
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
- *dataLength = bytes;
-
- return err;
-}
-
-OSStatus QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socket,
- const char *data, size_t *dataLength)
-{
- Q_ASSERT(socket);
- Q_ASSERT(data);
- Q_ASSERT(dataLength);
-
- QTcpSocket *plainSocket = socket->plainSocket;
- Q_ASSERT(plainSocket);
-
- const qint64 bytes = plainSocket->write(data, *dataLength);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "write" << bytes;
-#endif
- if (bytes < 0) {
- *dataLength = 0;
- return errSecIO;
- }
-
- const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : errSecSuccess;
- *dataLength = bytes;
-
- return err;
-}
-
-void QSslSocketPrivate::ensureInitialized()
-{
- const QMutexLocker locker(qt_securetransport_mutex);
- if (s_loadedCiphersAndCerts)
- return;
-
- // We have to set it before setDefaultSupportedCiphers,
- // since this function can trigger static (global)'s initialization
- // and as a result - recursive ensureInitialized call
- // from QSslCertificatePrivate's ctor.
- s_loadedCiphersAndCerts = true;
-
- const QSecureTransportContext context(qt_createSecureTransportContext(QSslSocket::SslClientMode));
- if (context) {
- QList<QSslCipher> ciphers;
- QList<QSslCipher> defaultCiphers;
-
- size_t numCiphers = 0;
- // Fails only if any of parameters is null.
- SSLGetNumberSupportedCiphers(context, &numCiphers);
- QVector<SSLCipherSuite> cfCiphers(numCiphers);
- // Fails only if any of parameter is null or number of ciphers is wrong.
- SSLGetSupportedCiphers(context, cfCiphers.data(), &numCiphers);
-
- for (size_t i = 0; i < size_t(cfCiphers.size()); ++i) {
- const QSslCipher ciph(QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers.at(i)));
- if (!ciph.isNull()) {
- ciphers << ciph;
- if (ciph.usedBits() >= 128)
- defaultCiphers << ciph;
- }
- }
-
- setDefaultSupportedCiphers(ciphers);
- setDefaultCiphers(defaultCiphers);
-
- if (!s_loadRootCertsOnDemand)
- setDefaultCaCertificates(systemCaCertificates());
- } else {
- s_loadedCiphersAndCerts = false;
- }
-
-}
-
-long QSslSocketPrivate::sslLibraryVersionNumber()
-{
- return 0;
-}
-
-QString QSslSocketPrivate::sslLibraryVersionString()
-{
- return QLatin1String("Secure Transport, ") + QSysInfo::prettyProductName();
-}
-
-long QSslSocketPrivate::sslLibraryBuildVersionNumber()
-{
- return 0;
-}
-
-QString QSslSocketPrivate::sslLibraryBuildVersionString()
-{
- return sslLibraryVersionString();
-}
-
-bool QSslSocketPrivate::supportsSsl()
-{
- return true;
-}
-
-void QSslSocketPrivate::resetDefaultCiphers()
-{
- Q_UNIMPLEMENTED();
-}
-
-void QSslSocketPrivate::resetDefaultEllipticCurves()
-{
- // No public API for this (?).
- Q_UNIMPLEMENTED();
-}
-
-QSslSocketBackendPrivate::QSslSocketBackendPrivate()
- : context(nullptr)
-{
-}
-
-QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
-{
- destroySslContext();
-}
-
-void QSslSocketBackendPrivate::continueHandshake()
-{
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "connection encrypted";
-#endif
- Q_Q(QSslSocket);
- connectionEncrypted = true;
-
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
- // Unlike OpenSSL, Secure Transport does not allow to negotiate protocols via
- // a callback during handshake. We can only set our list of preferred protocols
- // (and send it during handshake) and then receive what our peer has sent to us.
- // And here we can finally try to find a match (if any).
- if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
- const auto &requestedProtocols = configuration.nextAllowedProtocols;
- if (const int requestedCount = requestedProtocols.size()) {
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
- configuration.nextNegotiatedProtocol.clear();
-
- QCFType<CFArrayRef> cfArray;
- const OSStatus result = SSLCopyALPNProtocols(context, &cfArray);
- if (result == errSecSuccess && cfArray && CFArrayGetCount(cfArray)) {
- const int size = CFArrayGetCount(cfArray);
- QVector<QString> peerProtocols(size);
- for (int i = 0; i < size; ++i)
- peerProtocols[i] = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(cfArray, i));
-
- for (int i = 0; i < requestedCount; ++i) {
- const auto requestedName = QString::fromLatin1(requestedProtocols[i]);
- for (int j = 0; j < size; ++j) {
- if (requestedName == peerProtocols[j]) {
- configuration.nextNegotiatedProtocol = requestedName.toLatin1();
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
- break;
- }
- }
- if (configuration.nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNegotiated)
- break;
- }
- }
- }
- }
-#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE
-
- if (!renegotiating)
- emit q->encrypted();
-
- if (autoStartHandshake && pendingClose) {
- pendingClose = false;
- q->disconnectFromHost();
- }
-}
-
-void QSslSocketBackendPrivate::disconnected()
-{
- if (plainSocket->bytesAvailable() <= 0)
- destroySslContext();
- // If there is still buffered data in the plain socket, don't destroy the ssl context yet.
- // It will be destroyed when the socket is deleted.
-}
-
-void QSslSocketBackendPrivate::disconnectFromHost()
-{
- if (context) {
- if (!shutdown) {
- SSLClose(context);
- shutdown = true;
- }
- }
- plainSocket->disconnectFromHost();
-}
-
-QSslCipher QSslSocketBackendPrivate::sessionCipher() const
-{
- SSLCipherSuite cipher = 0;
- if (context && SSLGetNegotiatedCipher(context, &cipher) == errSecSuccess)
- return QSslCipher_from_SSLCipherSuite(cipher);
-
- return QSslCipher();
-}
-
-QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
-{
- if (!context)
- return QSsl::UnknownProtocol;
-
- SSLProtocol protocol = kSSLProtocolUnknown;
- const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol);
- if (err != errSecSuccess) {
- qCWarning(lcSsl) << "SSLGetNegotiatedProtocolVersion failed:" << err;
- return QSsl::UnknownProtocol;
- }
-
- switch (protocol) {
- case kSSLProtocol2:
- return QSsl::SslV2;
- case kSSLProtocol3:
- return QSsl::SslV3;
- case kTLSProtocol1:
- return QSsl::TlsV1_0;
- case kTLSProtocol11:
- return QSsl::TlsV1_1;
- case kTLSProtocol12:
- return QSsl::TlsV1_2;
- case kTLSProtocol13:
- return QSsl::TlsV1_3;
- default:
- return QSsl::UnknownProtocol;
- }
-}
-
-void QSslSocketBackendPrivate::startClientEncryption()
-{
- if (!initSslContext()) {
- // Error description/code were set, 'error' emitted
- // by initSslContext, but OpenSSL socket also sets error
- // emits a signal twice, so ...
- setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context"));
- return;
- }
-
- startHandshake();
-}
-
-void QSslSocketBackendPrivate::startServerEncryption()
-{
- if (!initSslContext()) {
- // Error description/code were set, 'error' emitted
- // by initSslContext, but OpenSSL socket also sets error
- // emits a signal twice, so ...
- setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Unable to init SSL Context"));
- return;
- }
-
- startHandshake();
-}
-
-void QSslSocketBackendPrivate::transmit()
-{
- Q_Q(QSslSocket);
-
- // If we don't have any SSL context, don't bother transmitting.
- // Edit: if SSL session closed, don't bother either.
- if (!context || shutdown)
- return;
-
- if (!isHandshakeComplete())
- startHandshake();
-
- if (isHandshakeComplete() && !writeBuffer.isEmpty()) {
- qint64 totalBytesWritten = 0;
- while (writeBuffer.nextDataBlockSize() > 0 && context) {
- const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize();
- size_t writtenBytes = 0;
- const OSStatus err = SSLWrite(context, writeBuffer.readPointer(), nextDataBlockSize, &writtenBytes);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "SSLWrite returned" << err;
-#endif
- if (err != errSecSuccess && err != errSSLWouldBlock) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QStringLiteral("SSLWrite failed: %1").arg(err));
- break;
- }
-
- if (writtenBytes) {
- writeBuffer.free(writtenBytes);
- totalBytesWritten += writtenBytes;
- }
-
- if (writtenBytes < nextDataBlockSize)
- break;
- }
-
- if (totalBytesWritten > 0) {
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- emittedBytesWritten = true;
- emit q->bytesWritten(totalBytesWritten);
- emittedBytesWritten = false;
- }
- emit q->channelBytesWritten(0, totalBytesWritten);
- }
- }
-
- if (isHandshakeComplete()) {
- QVarLengthArray<char, 4096> data;
- while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
- size_t readBytes = 0;
- data.resize(4096);
- const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "SSLRead returned" << err;
-#endif
- if (err == errSSLClosedGraceful) {
- shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- QSslSocket::tr("The TLS/SSL connection has been closed"));
- break;
- } else if (err != errSecSuccess && err != errSSLWouldBlock) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QStringLiteral("SSLRead failed: %1").arg(err));
- break;
- }
-
- if (err == errSSLWouldBlock && renegotiating) {
- startHandshake();
- break;
- }
-
- if (readBytes) {
- buffer.append(data.constData(), readBytes);
- if (readyReadEmittedPointer)
- *readyReadEmittedPointer = true;
- emit q->readyRead();
- emit q->channelReadyRead(0);
- }
-
- if (err == errSSLWouldBlock)
- break;
- }
- }
-}
-
-
-QList<QSslError> (QSslSocketBackendPrivate::verify)(QList<QSslCertificate> certificateChain, const QString &hostName)
-{
- Q_UNIMPLEMENTED();
- Q_UNUSED(certificateChain)
- Q_UNUSED(hostName)
-
- QList<QSslError> errors;
- errors << QSslError(QSslError::UnspecifiedError);
-
- return errors;
-}
-
-bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase)
-{
- Q_UNIMPLEMENTED();
- Q_UNUSED(device)
- Q_UNUSED(key)
- Q_UNUSED(cert)
- Q_UNUSED(caCertificates)
- Q_UNUSED(passPhrase)
- return false;
-}
-
-QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher)
-{
- QSslCipher ciph;
- switch (cipher) {
- // Sorted as in CipherSuite.h (and groupped by their RFC)
- case SSL_RSA_WITH_NULL_MD5:
- ciph.d->name = QLatin1String("NULL-MD5");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("NULL-SHA");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_RC4_128_MD5:
- ciph.d->name = QLatin1String("RC4-MD5");
- ciph.d->protocol = QSsl::SslV3;
- break;
- case SSL_RSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("RC4-SHA");
- ciph.d->protocol = QSsl::SslV3;
- break;
-
- // TLS addenda using AES, per RFC 3268
- case TLS_RSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("AES128-SHA");
- break;
- case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA");
- break;
- case TLS_RSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("AES256-SHA");
- break;
- case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA");
- break;
-
- // ECDSA addenda, RFC 4492
- case TLS_ECDH_ECDSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("ECDH-ECDSA-NULL-SHA");
- break;
- case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("ECDH-ECDSA-RC4-SHA");
- break;
- case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA");
- break;
- case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA");
- break;
- case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA");
- break;
- case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-NULL-SHA");
- break;
- case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-RC4-SHA");
- break;
- case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA");
- break;
- case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA");
- break;
- case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA");
- break;
- case TLS_ECDH_RSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("ECDH-RSA-NULL-SHA");
- break;
- case TLS_ECDH_RSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("ECDH-RSA-RC4-SHA");
- break;
- case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-RSA-DES-CBC3-SHA");
- break;
- case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA");
- break;
- case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA");
- break;
- case TLS_ECDHE_RSA_WITH_NULL_SHA:
- ciph.d->name = QLatin1String("ECDHE-RSA-NULL-SHA");
- break;
- case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- ciph.d->name = QLatin1String("ECDHE-RSA-RC4-SHA");
- break;
- case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA");
- break;
- case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA");
- break;
- case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA");
- break;
-
- // TLS 1.2 addenda, RFC 5246
- case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("DES-CBC3-SHA");
- break;
- case TLS_RSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("AES128-SHA256");
- break;
- case TLS_RSA_WITH_AES_256_CBC_SHA256:
- ciph.d->name = QLatin1String("AES256-SHA256");
- break;
- case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
- ciph.d->name = QLatin1String("DHE-RSA-DES-CBC3-SHA");
- break;
- case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA256");
- break;
- case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
- ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA256");
- break;
-
- // Addendum from RFC 4279, TLS PSK
- // all missing atm.
-
- // RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption
- // all missing atm.
-
- // Addenda from rfc 5288 AES Galois Counter Mode (CGM) Cipher Suites for TLS
- case TLS_RSA_WITH_AES_256_GCM_SHA384:
- ciph.d->name = QLatin1String("AES256-GCM-SHA384");
- break;
-
- // RFC 5487 - PSK with SHA-256/384 and AES GCM
- // all missing atm.
-
- // Addenda from rfc 5289 Elliptic Curve Cipher Suites with HMAC SHA-256/384
- case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA256");
- break;
- case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
- ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA384");
- break;
- case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA256");
- break;
- case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
- ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA384");
- break;
- case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA256");
- break;
- case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
- ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384");
- break;
- case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
- ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA256");
- break;
- case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
- ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA384");
- break;
-
- // Addenda from rfc 5289 Elliptic Curve Cipher Suites
- // with SHA-256/384 and AES Galois Counter Mode (GCM)
- case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
- ciph.d->name = QLatin1String("ECDHE-RSA-AES256-GCM-SHA384");
- break;
-
- default:
- return ciph;
- }
- ciph.d->isNull = false;
-
- // protocol
- if (ciph.d->protocol == QSsl::SslV3) {
- ciph.d->protocolString = QLatin1String("SSLv3");
- } else {
- ciph.d->protocol = QSsl::TlsV1_2;
- ciph.d->protocolString = QLatin1String("TLSv1.2");
- }
-
- const auto bits = ciph.d->name.splitRef(QLatin1Char('-'));
- if (bits.size() >= 2) {
- if (bits.size() == 2 || bits.size() == 3) {
- ciph.d->keyExchangeMethod = QLatin1String("RSA");
- } else if (bits.front() == QLatin1String("DH") || bits.front() == QLatin1String("DHE")) {
- ciph.d->keyExchangeMethod = QLatin1String("DH");
- } else if (bits.front() == QLatin1String("ECDH") || bits.front() == QLatin1String("ECDHE")) {
- ciph.d->keyExchangeMethod = QLatin1String("ECDH");
- } else {
- qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name;
- }
-
- if (bits.size() == 2 || bits.size() == 3) {
- ciph.d->authenticationMethod = QLatin1String("RSA");
- } else if (ciph.d->name.contains(QLatin1String("-ECDSA-"))) {
- ciph.d->authenticationMethod = QLatin1String("ECDSA");
- } else if (ciph.d->name.contains(QLatin1String("-RSA-"))) {
- ciph.d->authenticationMethod = QLatin1String("RSA");
- } else {
- qCWarning(lcSsl) << "Unknown Au" << ciph.d->name;
- }
-
- if (ciph.d->name.contains(QLatin1String("RC4-"))) {
- ciph.d->encryptionMethod = QLatin1String("RC4(128)");
- ciph.d->bits = 128;
- ciph.d->supportedBits = 128;
- } else if (ciph.d->name.contains(QLatin1String("DES-CBC3-"))) {
- ciph.d->encryptionMethod = QLatin1String("3DES(168)");
- ciph.d->bits = 168;
- ciph.d->supportedBits = 168;
- } else if (ciph.d->name.contains(QLatin1String("AES128-"))) {
- ciph.d->encryptionMethod = QLatin1String("AES(128)");
- ciph.d->bits = 128;
- ciph.d->supportedBits = 128;
- } else if (ciph.d->name.contains(QLatin1String("AES256-GCM"))) {
- ciph.d->encryptionMethod = QLatin1String("AESGCM(256)");
- ciph.d->bits = 256;
- ciph.d->supportedBits = 256;
- } else if (ciph.d->name.contains(QLatin1String("AES256-"))) {
- ciph.d->encryptionMethod = QLatin1String("AES(256)");
- ciph.d->bits = 256;
- ciph.d->supportedBits = 256;
- } else if (ciph.d->name.contains(QLatin1String("NULL-"))) {
- ciph.d->encryptionMethod = QLatin1String("NULL");
- } else {
- qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name;
- }
- }
- return ciph;
-}
-
-bool QSslSocketBackendPrivate::initSslContext()
-{
- Q_Q(QSslSocket);
-
- Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null");
- Q_ASSERT(plainSocket);
-
- context.reset(qt_createSecureTransportContext(mode));
- if (!context) {
- setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("SSLCreateContext failed"));
- return false;
- }
-
- const OSStatus err = SSLSetIOFuncs(context,
- reinterpret_cast<SSLReadFunc>(&QSslSocketBackendPrivate::ReadCallback),
- reinterpret_cast<SSLWriteFunc>(&QSslSocketBackendPrivate::WriteCallback));
- if (err != errSecSuccess) {
- destroySslContext();
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QStringLiteral("SSLSetIOFuncs failed: %1").arg(err));
- return false;
- }
-
- SSLSetConnection(context, this);
-
- if (mode == QSslSocket::SslServerMode
- && !configuration.localCertificateChain.isEmpty()) {
- QString errorDescription;
- QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
- if (!setSessionCertificate(errorDescription, errorCode)) {
- destroySslContext();
- setErrorAndEmit(errorCode, errorDescription);
- return false;
- }
- }
-
- if (!setSessionProtocol()) {
- destroySslContext();
- setErrorAndEmit(QAbstractSocket::SslInternalError, QStringLiteral("Failed to set protocol version"));
- return false;
- }
-
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13_4, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
- if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
- const auto protocolNames = configuration.nextAllowedProtocols;
- QCFType<CFMutableArrayRef> cfNames(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks));
- if (cfNames) {
- for (const QByteArray &name : protocolNames) {
- if (name.size() > 255) {
- qCWarning(lcSsl) << "TLS ALPN extension" << name
- << "is too long and will be ignored.";
- continue;
- } else if (name.isEmpty()) {
- continue;
- }
- QCFString cfName(QString::fromLatin1(name).toCFString());
- CFArrayAppendValue(cfNames, cfName);
- }
-
- if (CFArrayGetCount(cfNames)) {
- // Up to the application layer to check that negotiation
- // failed, and handle this non-TLS error, we do not handle
- // the result of this call as an error:
- if (SSLSetALPNProtocols(context, cfNames) != errSecSuccess)
- qCWarning(lcSsl) << "SSLSetALPNProtocols failed - too long protocol names?";
- }
- } else {
- qCWarning(lcSsl) << "failed to allocate ALPN names array";
- }
- }
-#endif // QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE
-
- if (mode == QSslSocket::SslClientMode) {
- // enable Server Name Indication (SNI)
- QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
- if (tlsHostName.isEmpty())
- tlsHostName = hostName;
-
- const QByteArray ace(QUrl::toAce(tlsHostName));
- SSLSetPeerDomainName(context, ace.data(), ace.size());
- // tell SecureTransport we handle peer verification ourselves
- OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
- if (err == errSecSuccess)
- err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true);
-
- if (err != errSecSuccess) {
- destroySslContext();
- setErrorAndEmit(QSslSocket::SslInternalError,
- QStringLiteral("SSLSetSessionOption failed: %1").arg(err));
- return false;
- }
- //
- } else {
- if (configuration.peerVerifyMode != QSslSocket::VerifyNone) {
- // kAlwaysAuthenticate - always fails even if we set break on client auth.
- OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate);
- if (err == errSecSuccess) {
- // We'd like to verify peer ourselves, otherwise handshake will
- // most probably fail before we can do anything.
- err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnClientAuth, true);
- }
-
- if (err != errSecSuccess) {
- destroySslContext();
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QStringLiteral("failed to set SSL context option in server mode: %1").arg(err));
- return false;
- }
- }
-#if !defined(QT_PLATFORM_UIKIT)
- // No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs.
- SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam));
-#endif
- }
- return true;
-}
-
-void QSslSocketBackendPrivate::destroySslContext()
-{
- context.reset(nullptr);
-}
-
-bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode)
-{
- Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
-
- QSslCertificate localCertificate;
- if (!configuration.localCertificateChain.isEmpty())
- localCertificate = configuration.localCertificateChain.at(0);
-
- if (!localCertificate.isNull()) {
- // Require a private key as well.
- if (configuration.privateKey.isNull()) {
- errorCode = QAbstractSocket::SslInvalidUserDataError;
- errorDescription = QStringLiteral("Cannot provide a certificate with no key");
- return false;
- }
-
- // import certificates and key
- const QString passPhrase(QString::fromLatin1("foobar"));
- QCFType<CFDataRef> pkcs12 = _q_makePkcs12(configuration.localCertificateChain,
- configuration.privateKey, passPhrase).toCFData();
- QCFType<CFStringRef> password = passPhrase.toCFString();
- const void *keys[2] = { kSecImportExportPassphrase };
- const void *values[2] = { password };
- CFIndex nKeys = 1;
-#ifdef Q_OS_MACOS
- bool envOk = false;
- const int env = qEnvironmentVariableIntValue("QT_SSL_USE_TEMPORARY_KEYCHAIN", &envOk);
- if (envOk && env) {
- static const EphemeralSecKeychain temporaryKeychain;
- if (temporaryKeychain.keychain) {
- nKeys = 2;
- keys[1] = kSecImportExportKeychain;
- values[1] = temporaryKeychain.keychain;
- }
- }
-#endif
- QCFType<CFDictionaryRef> options = CFDictionaryCreate(nullptr, keys, values, nKeys,
- nullptr, nullptr);
- QCFType<CFArrayRef> items;
- OSStatus err = SecPKCS12Import(pkcs12, options, &items);
- if (err != errSecSuccess) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl) << plainSocket
- << QStringLiteral("SecPKCS12Import failed: %1").arg(err);
-#endif
- errorCode = QAbstractSocket::SslInvalidUserDataError;
- errorDescription = QStringLiteral("SecPKCS12Import failed: %1").arg(err);
- return false;
- }
-
- if (!CFArrayGetCount(items)) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl) << plainSocket << "SecPKCS12Import returned no items";
-#endif
- errorCode = QAbstractSocket::SslInvalidUserDataError;
- errorDescription = QStringLiteral("SecPKCS12Import returned no items");
- return false;
- }
-
- CFDictionaryRef import = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
- SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(import, kSecImportItemIdentity);
- if (!identity) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl) << plainSocket << "SecPKCS12Import returned no identity";
-#endif
- errorCode = QAbstractSocket::SslInvalidUserDataError;
- errorDescription = QStringLiteral("SecPKCS12Import returned no identity");
- return false;
- }
-
- QCFType<CFMutableArrayRef> certs = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
- if (!certs) {
- errorCode = QAbstractSocket::SslInternalError;
- errorDescription = QStringLiteral("Failed to allocate certificates array");
- return false;
- }
-
- CFArrayAppendValue(certs, identity);
-
- CFArrayRef chain = (CFArrayRef)CFDictionaryGetValue(import, kSecImportItemCertChain);
- if (chain) {
- for (CFIndex i = 1, e = CFArrayGetCount(chain); i < e; ++i)
- CFArrayAppendValue(certs, CFArrayGetValueAtIndex(chain, i));
- }
-
- err = SSLSetCertificate(context, certs);
- if (err != errSecSuccess) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl) << plainSocket
- << QStringLiteral("Cannot set certificate and key: %1").arg(err);
-#endif
- errorCode = QAbstractSocket::SslInvalidUserDataError;
- errorDescription = QStringLiteral("Cannot set certificate and key: %1").arg(err);
- return false;
- }
- }
-
- return true;
-}
-
-bool QSslSocketBackendPrivate::setSessionProtocol()
-{
- Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
-
- // QSsl::SslV2 == kSSLProtocol2 is disabled in Secure Transport and
- // always fails with errSSLIllegalParam:
- // if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION)
- // return errSSLIllegalParam;
- // where MINIMUM_STREAM_VERSION is SSL_Version_3_0, MAXIMUM_STREAM_VERSION is TLS_Version_1_2.
- if (configuration.protocol == QSsl::SslV2) {
- qCDebug(lcSsl) << "protocol QSsl::SslV2 is disabled";
- return false;
- }
-
- // SslV3 is unsupported.
- if (configuration.protocol == QSsl::SslV3) {
- qCDebug(lcSsl) << "protocol QSsl::SslV3 is disabled";
- return false;
- }
-
- // SecureTransport has kTLSProtocol13 constant and also, kTLSProtocolMaxSupported.
- // Calling SSLSetProtocolVersionMax/Min with any of these two constants results
- // in errInvalidParam and a failure to set the protocol version. This means
- // no TLS 1.3 on macOS and iOS.
- switch (configuration.protocol) {
- case QSsl::TlsV1_3:
- case QSsl::TlsV1_3OrLater:
- qCWarning(lcSsl) << plainSocket << "SecureTransport does not support TLS 1.3";
- return false;
- default:;
- }
-
- OSStatus err = errSecSuccess;
-
- if (configuration.protocol == QSsl::TlsV1_0) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::TlsV1_1) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol11);
- } else if (configuration.protocol == QSsl::TlsV1_2) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
- } else if (configuration.protocol == QSsl::AnyProtocol) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : any";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::TlsV1SslV3) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- if (err == errSecSuccess)
- err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::SecureProtocols) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::TlsV1_0OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
- } else if (configuration.protocol == QSsl::TlsV1_1OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
- } else if (configuration.protocol == QSsl::TlsV1_2OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
- #endif
- err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
- } else {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "no protocol version found in the configuration";
- #endif
- return false;
- }
-
- return err == errSecSuccess;
-}
-
-bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const
-{
- const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode;
- return mode == QSslSocket::SslServerMode
- && (verifyMode == QSslSocket::QueryPeer
- || verifyMode == QSslSocket::AutoVerifyPeer
- || verifyMode == QSslSocket::VerifyNone);
-}
-
-bool QSslSocketBackendPrivate::verifySessionProtocol() const
-{
- bool protocolOk = false;
- if (configuration.protocol == QSsl::AnyProtocol)
- protocolOk = true;
- else if (configuration.protocol == QSsl::TlsV1SslV3)
- protocolOk = (sessionProtocol() == QSsl::TlsV1_0);
- else if (configuration.protocol == QSsl::SecureProtocols)
- protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
- else if (configuration.protocol == QSsl::TlsV1_0OrLater)
- protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
- else if (configuration.protocol == QSsl::TlsV1_1OrLater)
- protocolOk = (sessionProtocol() >= QSsl::TlsV1_1);
- else if (configuration.protocol == QSsl::TlsV1_2OrLater)
- protocolOk = (sessionProtocol() >= QSsl::TlsV1_2);
- else if (configuration.protocol == QSsl::TlsV1_3OrLater)
- protocolOk = (sessionProtocol() >= QSsl::TlsV1_3OrLater);
- else
- protocolOk = (sessionProtocol() == configuration.protocol);
-
- return protocolOk;
-}
-
-bool QSslSocketBackendPrivate::verifyPeerTrust()
-{
- Q_Q(QSslSocket);
-
- const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode;
- const bool canIgnoreVerify = canIgnoreTrustVerificationFailure();
-
- Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
- Q_ASSERT(plainSocket);
-
- QCFType<SecTrustRef> trust;
- OSStatus err = SSLCopyPeerTrust(context, &trust);
- // !trust - SSLCopyPeerTrust can return errSecSuccess but null trust.
- if (err != errSecSuccess || !trust) {
- if (!canIgnoreVerify) {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QStringLiteral("Failed to obtain peer trust: %1").arg(err));
- plainSocket->disconnectFromHost();
- return false;
- } else {
- return true;
- }
- }
-
- QList<QSslError> errors;
-
- // Store certificates.
- // Apple's docs say SetTrustEvaluate must be called before
- // SecTrustGetCertificateAtIndex, but this results
- // in 'kSecTrustResultRecoverableTrustFailure', so
- // here we just ignore 'res' (later we'll use SetAnchor etc.
- // and evaluate again).
- SecTrustResultType res = kSecTrustResultInvalid;
- err = SecTrustEvaluate(trust, &res);
- if (err != errSecSuccess) {
- // We can not ignore this, it's not even about trust verification
- // probably ...
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QStringLiteral("SecTrustEvaluate failed: %1").arg(err));
- plainSocket->disconnectFromHost();
- return false;
- }
-
- configuration.peerCertificate.clear();
- configuration.peerCertificateChain.clear();
-
- const CFIndex certCount = SecTrustGetCertificateCount(trust);
- for (CFIndex i = 0; i < certCount; ++i) {
- SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
- QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
- configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
- }
-
- if (configuration.peerCertificateChain.size())
- configuration.peerCertificate = configuration.peerCertificateChain.at(0);
-
- // Check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer):
- for (const QSslCertificate &cert : qAsConst(configuration.peerCertificateChain)) {
- if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) {
- const QSslError error(QSslError::CertificateBlacklisted, cert);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
-
- const bool doVerifyPeer = verifyMode == QSslSocket::VerifyPeer
- || (verifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
- // Check the peer certificate itself. First try the subject's common name
- // (CN) as a wildcard, then try all alternate subject name DNS entries the
- // same way.
- if (!configuration.peerCertificate.isNull()) {
- // but only if we're a client connecting to a server
- // if we're the server, don't check CN
- if (mode == QSslSocket::SslClientMode) {
- const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
- if (!isMatchingHostname(configuration.peerCertificate, peerName) && !canIgnoreVerify) {
- // No matches in common names or alternate names.
- const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- } else {
- // No peer certificate presented. Report as error if the socket
- // expected one.
- if (doVerifyPeer && !canIgnoreVerify) {
- const QSslError error(QSslError::NoPeerCertificate);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
-
- // verify certificate chain
- QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
- for (const QSslCertificate &cert : qAsConst(configuration.caCertificates)) {
- QCFType<CFDataRef> certData = cert.d->derData.toCFData();
- if (QCFType<SecCertificateRef> secRef = SecCertificateCreateWithData(nullptr, certData))
- CFArrayAppendValue(certArray, secRef);
- else
- qCWarning(lcSsl, "Failed to create SecCertificate from QSslCertificate");
- }
-
- SecTrustSetAnchorCertificates(trust, certArray);
-
- // By default SecTrustEvaluate uses both CA certificates provided in
- // QSslConfiguration and the ones from the system database. This behavior can
- // be unexpected if a user's code tries to limit the trusted CAs to those
- // explicitly set in QSslConfiguration.
- // Since on macOS we initialize the default QSslConfiguration copying the
- // system CA certificates (using SecTrustSettingsCopyCertificates) we can
- // call SecTrustSetAnchorCertificatesOnly(trust, true) to force SecTrustEvaluate
- // to use anchors only from our QSslConfiguration.
- // Unfortunately, SecTrustSettingsCopyCertificates is not available on iOS
- // and the default QSslConfiguration always has an empty list of system CA
- // certificates. This leaves no way to provide client code with access to the
- // actual system CA certificate list (which most use-cases need) other than
- // by letting SecTrustEvaluate fall through to the system list; so, in this case
- // (even though the client code may have provided its own certs), we retain
- // the default behavior. Note, with macOS SDK below 10.12 using 'trust my
- // anchors only' may result in some valid chains rejected, apparently the
- // ones containing intermediated certificates; so we use this functionality
- // on more recent versions only.
-
- bool anchorsFromConfigurationOnly = false;
-
-#ifdef Q_OS_MACOS
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra)
- anchorsFromConfigurationOnly = true;
-#endif // Q_OS_MACOS
-
- SecTrustSetAnchorCertificatesOnly(trust, anchorsFromConfigurationOnly);
-
- SecTrustResultType trustResult = kSecTrustResultInvalid;
- SecTrustEvaluate(trust, &trustResult);
- switch (trustResult) {
- case kSecTrustResultUnspecified:
- case kSecTrustResultProceed:
- break;
- default:
- if (!canIgnoreVerify) {
- const QSslError error(QSslError::CertificateUntrusted, configuration.peerCertificate);
- errors << error;
- emit q->peerVerifyError(error);
- }
- }
-
- // report errors
- if (!errors.isEmpty() && !canIgnoreVerify) {
- sslErrors = errors;
- // checkSslErrors unconditionally emits sslErrors:
- // a user's slot can abort/close/disconnect on this
- // signal, so we also test the socket's state:
- if (!checkSslErrors() || q->state() != QAbstractSocket::ConnectedState)
- return false;
- } else {
- sslErrors.clear();
- }
-
- return true;
-}
-
-/*
- Copied verbatim from qsslsocket_openssl.cpp
-*/
-bool QSslSocketBackendPrivate::checkSslErrors()
-{
- Q_Q(QSslSocket);
- if (sslErrors.isEmpty())
- return true;
-
- emit q->sslErrors(sslErrors);
-
- const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
- const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
- // check whether we need to emit an SSL handshake error
- if (doVerifyPeer && doEmitSslError) {
- if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
- pauseSocketNotifiers(q);
- paused = true;
- } else {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- sslErrors.constFirst().errorString());
- plainSocket->disconnectFromHost();
- }
- return false;
- }
-
- return true;
-}
-
-bool QSslSocketBackendPrivate::startHandshake()
-{
- Q_ASSERT(context);
- Q_Q(QSslSocket);
-
- OSStatus err = SSLHandshake(context);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "SSLHandhake returned" << err;
-#endif
-
- if (err == errSSLWouldBlock) {
- // startHandshake has to be called again ... later.
- return false;
- } else if (err == errSSLServerAuthCompleted) {
- // errSSLServerAuthCompleted is a define for errSSLPeerAuthCompleted,
- // it works for both server/client modes.
- // In future we'll evaluate peer's trust at this point,
- // for now we just continue.
- // if (!verifyPeerTrust())
- // ...
- return startHandshake();
- } else if (err == errSSLClientCertRequested) {
- Q_ASSERT(mode == QSslSocket::SslClientMode);
- QString errorDescription;
- QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
- // setSessionCertificate does not fail if we have no certificate.
- // Failure means a real error (invalid certificate, no private key, etc).
- if (!setSessionCertificate(errorDescription, errorCode)) {
- setErrorAndEmit(errorCode, errorDescription);
- renegotiating = false;
- return false;
- } else {
- // We try to resume a handshake, even if have no
- // local certificates ... (up to server to deal with our failure).
- return startHandshake();
- }
- } else if (err != errSecSuccess) {
- if (err == errSSLBadCert && canIgnoreTrustVerificationFailure()) {
- // We're on the server side and client did not provide any
- // certificate. This is the new 'nice' error returned by
- // Security Framework after it was recently updated.
- return startHandshake();
- }
-
- renegotiating = false;
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QStringLiteral("SSLHandshake failed: %1").arg(err));
- plainSocket->disconnectFromHost();
- return false;
- }
-
- // Connection aborted during handshake phase.
- if (q->state() != QAbstractSocket::ConnectedState) {
- qCDebug(lcSsl) << "connection aborted";
- renegotiating = false;
- return false;
- }
-
- // check protocol version ourselves, as Secure Transport does not enforce
- // the requested min / max versions.
- if (!verifySessionProtocol()) {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, QStringLiteral("Protocol version mismatch"));
- plainSocket->disconnectFromHost();
- renegotiating = false;
- return false;
- }
-
- if (verifyPeerTrust()) {
- continueHandshake();
- renegotiating = false;
- return true;
- } else {
- renegotiating = false;
- return false;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
deleted file mode 100644
index 48aca964a1..0000000000
--- a/src/network/ssl/qsslsocket_mac_p.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_MAC_P_H
-#define QSSLSOCKET_MAC_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QtNetwork library. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qglobal.h>
-#include <QtCore/qlist.h>
-
-#include "qabstractsocket.h"
-#include "qsslsocket_p.h"
-
-#include <Security/Security.h>
-#include <Security/SecureTransport.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSecureTransportContext
-{
-public:
- explicit QSecureTransportContext(SSLContextRef context);
- ~QSecureTransportContext();
-
- operator SSLContextRef () const;
- void reset(SSLContextRef newContext);
-private:
- SSLContextRef context;
-
- Q_DISABLE_COPY_MOVE(QSecureTransportContext)
-};
-
-class QSslSocketBackendPrivate : public QSslSocketPrivate
-{
- Q_DECLARE_PUBLIC(QSslSocket)
-public:
- QSslSocketBackendPrivate();
- virtual ~QSslSocketBackendPrivate();
-
- // Final-overriders (QSslSocketPrivate):
- void continueHandshake() override;
- void disconnected() override;
- void disconnectFromHost() override;
- QSslCipher sessionCipher() const override;
- QSsl::SslProtocol sessionProtocol() const override;
- void startClientEncryption() override;
- void startServerEncryption() override;
- void transmit() override;
-
- static QList<QSslError> verify(QList<QSslCertificate> certificateChain,
- const QString &hostName);
-
- static bool importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase);
-
- static QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher);
-
-private:
- // SSL context management/properties:
- bool initSslContext();
- void destroySslContext();
- bool setSessionCertificate(QString &errorDescription,
- QAbstractSocket::SocketError &errorCode);
- bool setSessionProtocol();
- // Aux. functions to do a verification during handshake phase:
- bool canIgnoreTrustVerificationFailure() const;
- bool verifySessionProtocol() const;
- bool verifyPeerTrust();
-
- bool checkSslErrors();
- bool startHandshake();
-
- bool isHandshakeComplete() const {return connectionEncrypted && !renegotiating;}
-
- // IO callbacks:
- static OSStatus ReadCallback(QSslSocketBackendPrivate *socket, char *data, size_t *dataLength);
- static OSStatus WriteCallback(QSslSocketBackendPrivate *plainSocket, const char *data, size_t *dataLength);
-
- QSecureTransportContext context;
- bool renegotiating = false;
-
- Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp
deleted file mode 100644
index 4f0adf16f9..0000000000
--- a/src/network/ssl/qsslsocket_mac_shared.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 ownCloud Inc
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//#define QSSLSOCKET_DEBUG
-//#define QT_DECRYPT_SSL_TRAFFIC
-
-#include "qssl_p.h"
-#include "qsslsocket.h"
-
-#ifndef QT_NO_OPENSSL
-# include "qsslsocket_openssl_p.h"
-# include "qsslsocket_openssl_symbols_p.h"
-#endif
-
-#include "qsslcertificate_p.h"
-
-#ifdef Q_OS_DARWIN
-# include <private/qcore_mac_p.h>
-#endif
-
-#include <QtCore/qdebug.h>
-
-#ifdef Q_OS_OSX
-# include <Security/Security.h>
-#endif
-
-
-QT_BEGIN_NAMESPACE
-
-#ifdef Q_OS_OSX
-namespace {
-
-bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) {
- QCFType<CFDictionaryRef> policyProps = SecPolicyCopyProperties(policy);
- // only accept certificates with policies for SSL server validation for now
- if (CFEqual(CFDictionaryGetValue(policyProps, kSecPolicyOid), kSecPolicyAppleSSL)) {
- CFBooleanRef policyClient;
- if (CFDictionaryGetValueIfPresent(policyProps, kSecPolicyClient, reinterpret_cast<const void**>(&policyClient)) &&
- CFEqual(policyClient, kCFBooleanTrue)) {
- return false; // no client certs
- }
- if (!CFDictionaryContainsKey(props, kSecTrustSettingsResult)) {
- // as per the docs, no trust settings result implies full trust
- return true;
- }
- CFNumberRef number = static_cast<CFNumberRef>(CFDictionaryGetValue(props, kSecTrustSettingsResult));
- SecTrustSettingsResult settingsResult;
- CFNumberGetValue(number, kCFNumberSInt32Type, &settingsResult);
- switch (settingsResult) {
- case kSecTrustSettingsResultTrustRoot:
- case kSecTrustSettingsResultTrustAsRoot:
- return true;
- default:
- return false;
- }
- }
- return false;
-}
-
-bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
-{
- QCFType<CFArrayRef> cfTrustSettings;
- OSStatus status = SecTrustSettingsCopyTrustSettings(cfCert, SecTrustSettingsDomain(domain), &cfTrustSettings);
- if (status == noErr) {
- CFIndex size = CFArrayGetCount(cfTrustSettings);
- // if empty, trust for everything (as per the Security Framework documentation)
- if (size == 0) {
- return true;
- } else {
- for (CFIndex i = 0; i < size; ++i) {
- CFDictionaryRef props = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(cfTrustSettings, i));
- if (CFDictionaryContainsKey(props, kSecTrustSettingsPolicy)) {
- if (hasTrustedSslServerPolicy((SecPolicyRef)CFDictionaryGetValue(props, kSecTrustSettingsPolicy), props))
- return true;
- }
- }
- }
- } else {
- qCWarning(lcSsl, "Error receiving trust for a CA certificate");
- }
- return false;
-}
-
-} // anon namespace
-#endif // Q_OS_OSX
-
-QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
-{
- ensureInitialized();
-
- QList<QSslCertificate> systemCerts;
- // SecTrustSettingsCopyCertificates is not defined on iOS.
-#ifdef Q_OS_OSX
- // iterate through all enum members, order:
- // kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem
- for (int dom = kSecTrustSettingsDomainUser; dom <= int(kSecTrustSettingsDomainSystem); dom++) {
- QCFType<CFArrayRef> cfCerts;
- OSStatus status = SecTrustSettingsCopyCertificates(SecTrustSettingsDomain(dom), &cfCerts);
- if (status == noErr) {
- const CFIndex size = CFArrayGetCount(cfCerts);
- for (CFIndex i = 0; i < size; ++i) {
- SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
- QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
- if (isCaCertificateTrusted(cfCert, dom)) {
- if (derData == NULL) {
- qCWarning(lcSsl, "Error retrieving a CA certificate from the system store");
- } else {
- systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
- }
- }
- }
- }
- }
-#endif
- return systemCerts;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
deleted file mode 100644
index 8fbbffcaca..0000000000
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ /dev/null
@@ -1,2131 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2014 Governikus GmbH & Co. KG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-//#define QSSLSOCKET_DEBUG
-
-#include "qssl_p.h"
-#include "qsslsocket_openssl_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-#include "qsslsocket.h"
-#include "qsslcertificate_p.h"
-#include "qsslcipher_p.h"
-#include "qsslkey_p.h"
-#include "qsslellipticcurve.h"
-#include "qsslpresharedkeyauthenticator.h"
-#include "qsslpresharedkeyauthenticator_p.h"
-#include "qocspresponse_p.h"
-#include "qsslkey.h"
-
-#ifdef Q_OS_WIN
-#include "qwindowscarootfetcher_p.h"
-#endif
-
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qthread.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qscopedvaluerollback.h>
-#include <QtCore/qlibrary.h>
-#include <QtCore/qoperatingsystemversion.h>
-
-#if QT_CONFIG(ocsp)
-#include "qocsp_p.h"
-#endif
-
-#include <algorithm>
-#include <memory>
-
-#include <string.h>
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(QRecursiveMutex, qt_opensslInitMutex)
-
-bool QSslSocketPrivate::s_libraryLoaded = false;
-bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
-bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
-int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1;
-
-QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
-{
- QString errorString;
- char buf[256] = {}; // OpenSSL docs claim both 120 and 256; use the larger.
- unsigned long errNum;
- while ((errNum = q_ERR_get_error())) {
- if (!errorString.isEmpty())
- errorString.append(QLatin1String(", "));
- q_ERR_error_string_n(errNum, buf, sizeof buf);
- errorString.append(QString::fromLatin1(buf)); // error is ascii according to man ERR_error_string
- }
- return errorString;
-}
-
-extern "C" {
-
-#ifndef OPENSSL_NO_PSK
-static unsigned int q_ssl_psk_client_callback(SSL *ssl,
- const char *hint,
- char *identity, unsigned int max_identity_len,
- unsigned char *psk, unsigned int max_psk_len)
-{
- QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
- Q_ASSERT(d);
- return d->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
-}
-
-static unsigned int q_ssl_psk_server_callback(SSL *ssl,
- const char *identity,
- unsigned char *psk, unsigned int max_psk_len)
-{
- QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
- Q_ASSERT(d);
- return d->tlsPskServerCallback(identity, psk, max_psk_len);
-}
-
-#ifdef TLS1_3_VERSION
-static unsigned int q_ssl_psk_restore_client(SSL *ssl,
- const char *hint,
- char *identity, unsigned int max_identity_len,
- unsigned char *psk, unsigned int max_psk_len)
-{
- Q_UNUSED(hint);
- Q_UNUSED(identity);
- Q_UNUSED(max_identity_len);
- Q_UNUSED(psk);
- Q_UNUSED(max_psk_len);
-
-#ifdef QT_DEBUG
- QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
- Q_ASSERT(d);
- Q_ASSERT(d->mode == QSslSocket::SslClientMode);
-#endif
- q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
-
- return 0;
-}
-
-static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
- size_t *idlen, SSL_SESSION **sess)
-{
- Q_UNUSED(ssl);
- Q_UNUSED(md);
- Q_UNUSED(id);
- Q_UNUSED(idlen);
- Q_UNUSED(sess);
-
-#ifdef QT_DEBUG
- QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
- Q_ASSERT(d);
- Q_ASSERT(d->mode == QSslSocket::SslClientMode);
-#endif
-
- // Temporarily rebind the psk because it will be called next. The function will restore it.
- q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client);
-
- return 1; // need to return 1 or else "the connection setup fails."
-}
-#endif // TLS1_3_VERSION
-
-#endif // !OPENSSL_NO_PSK
-
-#if QT_CONFIG(ocsp)
-
-int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
-{
- Q_UNUSED(ocspRequest)
- if (!ssl)
- return SSL_TLSEXT_ERR_ALERT_FATAL;
-
- auto d = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
- if (!d)
- return SSL_TLSEXT_ERR_ALERT_FATAL;
-
- Q_ASSERT(d->mode == QSslSocket::SslServerMode);
- const QByteArray &response = d->ocspResponseDer;
- Q_ASSERT(response.size());
-
- unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size())));
- if (!derCopy)
- return SSL_TLSEXT_ERR_ALERT_FATAL;
-
- std::copy(response.data(), response.data() + response.size(), derCopy);
- // We don't check the return value: internally OpenSSL simply assignes the
- // pointer (it assumes it now owns this memory btw!) and the length.
- q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
-
- return SSL_TLSEXT_ERR_OK;
-}
-
-#endif // ocsp
-
-} // extern "C"
-
-QSslSocketBackendPrivate::QSslSocketBackendPrivate()
- : ssl(nullptr),
- readBio(nullptr),
- writeBio(nullptr),
- session(nullptr)
-{
- // Calls SSL_library_init().
- ensureInitialized();
-}
-
-QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
-{
- destroySslContext();
-}
-
-QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher)
-{
- QSslCipher ciph;
-
- char buf [256];
- QString descriptionOneLine = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf)));
-
- const auto descriptionList = descriptionOneLine.splitRef(QLatin1Char(' '), QString::SkipEmptyParts);
- if (descriptionList.size() > 5) {
- // ### crude code.
- ciph.d->isNull = false;
- ciph.d->name = descriptionList.at(0).toString();
-
- QString protoString = descriptionList.at(1).toString();
- ciph.d->protocolString = protoString;
- ciph.d->protocol = QSsl::UnknownProtocol;
- if (protoString == QLatin1String("SSLv3"))
- ciph.d->protocol = QSsl::SslV3;
- else if (protoString == QLatin1String("SSLv2"))
- ciph.d->protocol = QSsl::SslV2;
- else if (protoString == QLatin1String("TLSv1"))
- ciph.d->protocol = QSsl::TlsV1_0;
- else if (protoString == QLatin1String("TLSv1.1"))
- ciph.d->protocol = QSsl::TlsV1_1;
- else if (protoString == QLatin1String("TLSv1.2"))
- ciph.d->protocol = QSsl::TlsV1_2;
- else if (protoString == QLatin1String("TLSv1.3"))
- ciph.d->protocol = QSsl::TlsV1_3;
-
- if (descriptionList.at(2).startsWith(QLatin1String("Kx=")))
- ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString();
- if (descriptionList.at(3).startsWith(QLatin1String("Au=")))
- ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString();
- if (descriptionList.at(4).startsWith(QLatin1String("Enc=")))
- ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString();
- ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export"));
-
- ciph.d->bits = q_SSL_CIPHER_get_bits(cipher, &ciph.d->supportedBits);
- }
- return ciph;
-}
-
-QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx)
-{
- return {
- q_X509_STORE_CTX_get_error(ctx),
- q_X509_STORE_CTX_get_error_depth(ctx)
- };
-}
-
-#if QT_CONFIG(ocsp)
-
-QSslError qt_OCSP_response_status_to_QSslError(long code)
-{
- switch (code) {
- case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST:
- return QSslError::OcspMalformedRequest;
- case OCSP_RESPONSE_STATUS_INTERNALERROR:
- return QSslError::OcspInternalError;
- case OCSP_RESPONSE_STATUS_TRYLATER:
- return QSslError::OcspTryLater;
- case OCSP_RESPONSE_STATUS_SIGREQUIRED:
- return QSslError::OcspSigRequred;
- case OCSP_RESPONSE_STATUS_UNAUTHORIZED:
- return QSslError::OcspUnauthorized;
- case OCSP_RESPONSE_STATUS_SUCCESSFUL:
- default:
- return {};
- }
- Q_UNREACHABLE();
-}
-
-QOcspRevocationReason qt_OCSP_revocation_reason(int reason)
-{
- switch (reason) {
- case OCSP_REVOKED_STATUS_NOSTATUS:
- return QOcspRevocationReason::None;
- case OCSP_REVOKED_STATUS_UNSPECIFIED:
- return QOcspRevocationReason::Unspecified;
- case OCSP_REVOKED_STATUS_KEYCOMPROMISE:
- return QOcspRevocationReason::KeyCompromise;
- case OCSP_REVOKED_STATUS_CACOMPROMISE:
- return QOcspRevocationReason::CACompromise;
- case OCSP_REVOKED_STATUS_AFFILIATIONCHANGED:
- return QOcspRevocationReason::AffiliationChanged;
- case OCSP_REVOKED_STATUS_SUPERSEDED:
- return QOcspRevocationReason::Superseded;
- case OCSP_REVOKED_STATUS_CESSATIONOFOPERATION:
- return QOcspRevocationReason::CessationOfOperation;
- case OCSP_REVOKED_STATUS_CERTIFICATEHOLD:
- return QOcspRevocationReason::CertificateHold;
- case OCSP_REVOKED_STATUS_REMOVEFROMCRL:
- return QOcspRevocationReason::RemoveFromCRL;
- default:
- return QOcspRevocationReason::None;
- }
-
- Q_UNREACHABLE();
-}
-
-bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert, X509 *issuer)
-{
- // OCSP_basic_verify does verify that the responder is legit, the response is
- // correctly signed, CertID is correct. But it does not know which certificate
- // we were presented with by our peer, so it does not check if it's a response
- // for our peer's certificate.
- Q_ASSERT(singleResponse && peerCert && issuer);
-
- const OCSP_CERTID *certId = q_OCSP_SINGLERESP_get0_id(singleResponse); // Does not increment refcount.
- if (!certId) {
- qCWarning(lcSsl, "A SingleResponse without CertID");
- return false;
- }
-
- ASN1_OBJECT *md = nullptr;
- ASN1_INTEGER *reportedSerialNumber = nullptr;
- const int result = q_OCSP_id_get0_info(nullptr, &md, nullptr, &reportedSerialNumber, const_cast<OCSP_CERTID *>(certId));
- if (result != 1 || !md || !reportedSerialNumber) {
- qCWarning(lcSsl, "Failed to extract a hash and serial number from CertID structure");
- return false;
- }
-
- if (!q_X509_get_serialNumber(peerCert)) {
- // Is this possible at all? But we have to check this,
- // ASN1_INTEGER_cmp (called from OCSP_id_cmp) dereferences
- // without any checks at all.
- qCWarning(lcSsl, "No serial number in peer's ceritificate");
- return false;
- }
-
- const int nid = q_OBJ_obj2nid(md);
- if (nid == NID_undef) {
- qCWarning(lcSsl, "Unknown hash algorithm in CertID");
- return false;
- }
-
- const EVP_MD *digest = q_EVP_get_digestbynid(nid); // Does not increment refcount.
- if (!digest) {
- qCWarning(lcSsl) << "No digest for nid" << nid;
- return false;
- }
-
- OCSP_CERTID *recreatedId = q_OCSP_cert_to_id(digest, peerCert, issuer);
- if (!recreatedId) {
- qCWarning(lcSsl, "Failed to re-create CertID");
- return false;
- }
- const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
-
- if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
- qDebug(lcSsl, "Certificate ID mismatch");
- return false;
- }
- // Bingo!
- return true;
-}
-
-#endif // ocsp
-
-int q_X509Callback(int ok, X509_STORE_CTX *ctx)
-{
- if (!ok) {
- // Store the error and at which depth the error was detected.
-
- using ErrorListPtr = QVector<QSslErrorEntry>*;
- ErrorListPtr errors = nullptr;
-
- // Error list is attached to either 'SSL' or 'X509_STORE'.
- if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) // We try store first:
- errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
-
- if (!errors) {
- // Not found on store? Try SSL and its external data then. According to the OpenSSL's
- // documentation:
- //
- // "Whenever a X509_STORE_CTX object is created for the verification of the peers certificate
- // during a handshake, a pointer to the SSL object is stored into the X509_STORE_CTX object
- // to identify the connection affected. To retrieve this pointer the X509_STORE_CTX_get_ex_data()
- // function can be used with the correct index."
- if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
- errors = ErrorListPtr(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData + 1));
- }
-
- if (!errors) {
- qCWarning(lcSsl, "Neither X509_STORE, nor SSL contains error list, handshake failure");
- return 0;
- }
-
- errors->append(QSslErrorEntry::fromStoreContext(ctx));
- }
- // Always return OK to allow verification to continue. We handle the
- // errors gracefully after collecting all errors, after verification has
- // completed.
- return 1;
-}
-
-static void q_loadCiphersForConnection(SSL *connection, QList<QSslCipher> &ciphers,
- QList<QSslCipher> &defaultCiphers)
-{
- Q_ASSERT(connection);
-
- STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(connection);
- for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
- if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
- QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
- if (!ciph.isNull()) {
- // Unconditionally exclude ADH and AECDH ciphers since they offer no MITM protection
- if (!ciph.name().toLower().startsWith(QLatin1String("adh")) &&
- !ciph.name().toLower().startsWith(QLatin1String("exp-adh")) &&
- !ciph.name().toLower().startsWith(QLatin1String("aecdh"))) {
- ciphers << ciph;
-
- if (ciph.usedBits() >= 128)
- defaultCiphers << ciph;
- }
- }
- }
- }
-}
-
-// Defined in qsslsocket.cpp
-void q_setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
-
-long QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions)
-{
- long options;
- if (protocol == QSsl::TlsV1SslV3)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::SecureProtocols)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::TlsV1_0OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
- else if (protocol == QSsl::TlsV1_1OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1;
- else if (protocol == QSsl::TlsV1_2OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1;
- else if (protocol == QSsl::TlsV1_3OrLater)
- options = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2;
- else
- options = SSL_OP_ALL;
-
- // This option is disabled by default, so we need to be able to clear it
- if (sslOptions & QSsl::SslOptionDisableEmptyFragments)
- options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
- else
- options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
-
-#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
- // This option is disabled by default, so we need to be able to clear it
- if (sslOptions & QSsl::SslOptionDisableLegacyRenegotiation)
- options &= ~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
- else
- options |= SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-#endif
-
-#ifdef SSL_OP_NO_TICKET
- if (sslOptions & QSsl::SslOptionDisableSessionTickets)
- options |= SSL_OP_NO_TICKET;
-#endif
-#ifdef SSL_OP_NO_COMPRESSION
- if (sslOptions & QSsl::SslOptionDisableCompression)
- options |= SSL_OP_NO_COMPRESSION;
-#endif
-
- if (!(sslOptions & QSsl::SslOptionDisableServerCipherPreference))
- options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
-
- return options;
-}
-
-bool QSslSocketBackendPrivate::initSslContext()
-{
- Q_Q(QSslSocket);
-
- // If no external context was set (e.g. by QHttpNetworkConnection) we will
- // create a default context
- if (!sslContextPointer) {
- // create a deep copy of our configuration
- QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration);
- configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
- sslContextPointer = QSslContext::sharedFromConfiguration(mode, configurationCopy, allowRootCertOnDemandLoading);
- }
-
- if (sslContextPointer->error() != QSslError::NoError) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, sslContextPointer->errorString());
- sslContextPointer.clear(); // deletes the QSslContext
- return false;
- }
-
- // Create and initialize SSL session
- if (!(ssl = sslContextPointer->createSsl())) {
- // ### Bad error code
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl()));
- return false;
- }
-
- if (configuration.protocol != QSsl::SslV2 &&
- configuration.protocol != QSsl::SslV3 &&
- configuration.protocol != QSsl::UnknownProtocol &&
- mode == QSslSocket::SslClientMode) {
- // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
- QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
- if (tlsHostName.isEmpty())
- tlsHostName = hostName;
- QByteArray ace = QUrl::toAce(tlsHostName);
- // only send the SNI header if the URL is valid and not an IP
- if (!ace.isEmpty()
- && !QHostAddress().setAddress(tlsHostName)
- && !(configuration.sslOptions & QSsl::SslOptionDisableServerNameIndication)) {
- // We don't send the trailing dot from the host header if present see
- // https://tools.ietf.org/html/rfc6066#section-3
- if (ace.endsWith('.'))
- ace.chop(1);
- if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data()))
- qCWarning(lcSsl, "could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
- }
- }
-
- // Clear the session.
- errorList.clear();
-
- // Initialize memory BIOs for encryption and decryption.
- readBio = q_BIO_new(q_BIO_s_mem());
- writeBio = q_BIO_new(q_BIO_s_mem());
- if (!readBio || !writeBio) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error creating SSL session: %1").arg(getErrorsFromOpenSsl()));
- return false;
- }
-
- // Assign the bios.
- q_SSL_set_bio(ssl, readBio, writeBio);
-
- if (mode == QSslSocket::SslClientMode)
- q_SSL_set_connect_state(ssl);
- else
- q_SSL_set_accept_state(ssl);
-
- q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this);
-
-#ifndef OPENSSL_NO_PSK
- // Set the client callback for PSK
- if (mode == QSslSocket::SslClientMode)
- q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
- else if (mode == QSslSocket::SslServerMode)
- q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback);
-
-#if OPENSSL_VERSION_NUMBER >= 0x10101006L
- // Set the client callback for TLSv1.3 PSK
- if (mode == QSslSocket::SslClientMode
- && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) {
- q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback);
- }
-#endif // openssl version >= 0x10101006L
-
-#endif // OPENSSL_NO_PSK
-
-
-#if QT_CONFIG(ocsp)
- if (configuration.ocspStaplingEnabled) {
- if (mode == QSslSocket::SslServerMode) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QSslSocket::tr("Server-side QSslSocket does not support OCSP stapling"));
- return false;
- }
- if (q_SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp) != 1) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Failed to enable OCSP stapling"));
- return false;
- }
- }
-
- ocspResponseDer.clear();
- auto responsePos = configuration.backendConfig.find("Qt-OCSP-response");
- if (responsePos != configuration.backendConfig.end()) {
- // This is our private, undocumented 'API' we use for the auto-testing of
- // OCSP-stapling. It must be a der-encoded OCSP response, presumably set
- // by tst_QOcsp.
- const QVariant data(responsePos.value());
- if (data.canConvert<QByteArray>())
- ocspResponseDer = data.toByteArray();
- }
-
- if (ocspResponseDer.size()) {
- if (mode != QSslSocket::SslServerMode) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QSslSocket::tr("Client-side sockets do not send OCSP responses"));
- return false;
- }
- }
-#endif // ocsp
-
- return true;
-}
-
-void QSslSocketBackendPrivate::destroySslContext()
-{
- if (ssl) {
- // We do not send a shutdown alert here. Just mark the session as
- // resumable for qhttpnetworkconnection's "optimization", otherwise
- // OpenSSL won't start a session resumption.
- q_SSL_shutdown(ssl);
- q_SSL_free(ssl);
- ssl = nullptr;
- }
- sslContextPointer.clear();
-}
-
-/*!
- \internal
-
- Does the minimum amount of initialization to determine whether SSL
- is supported or not.
-*/
-
-bool QSslSocketPrivate::supportsSsl()
-{
- return ensureLibraryLoaded();
-}
-
-
-/*!
- \internal
-
- Returns the version number of the SSL library in use. Note that
- this is the version of the library in use at run-time, not compile
- time.
-*/
-long QSslSocketPrivate::sslLibraryVersionNumber()
-{
- if (!supportsSsl())
- return 0;
-
- return q_OpenSSL_version_num();
-}
-
-/*!
- \internal
-
- Returns the version string of the SSL library in use. Note that
- this is the version of the library in use at run-time, not compile
- time. If no SSL support is available then this will return an empty value.
-*/
-QString QSslSocketPrivate::sslLibraryVersionString()
-{
- if (!supportsSsl())
- return QString();
-
- const char *versionString = q_OpenSSL_version(OPENSSL_VERSION);
- if (!versionString)
- return QString();
-
- return QString::fromLatin1(versionString);
-}
-
-/*!
- \internal
-
- Declared static in QSslSocketPrivate, makes sure the SSL libraries have
- been initialized.
-*/
-void QSslSocketPrivate::ensureInitialized()
-{
- if (!supportsSsl())
- return;
-
- ensureCiphersAndCertsLoaded();
-}
-
-/*!
- \internal
-
- Returns the version number of the SSL library in use at compile
- time.
-*/
-long QSslSocketPrivate::sslLibraryBuildVersionNumber()
-{
- return OPENSSL_VERSION_NUMBER;
-}
-
-/*!
- \internal
-
- Returns the version string of the SSL library in use at compile
- time.
-*/
-QString QSslSocketPrivate::sslLibraryBuildVersionString()
-{
- // Using QStringLiteral to store the version string as unicode and
- // avoid false positives from Google searching the playstore for old
- // SSL versions. See QTBUG-46265
- return QStringLiteral(OPENSSL_VERSION_TEXT);
-}
-
-/*!
- \internal
-
- Declared static in QSslSocketPrivate, backend-dependent loading of
- application-wide global ciphers.
-*/
-void QSslSocketPrivate::resetDefaultCiphers()
-{
- SSL_CTX *myCtx = q_SSL_CTX_new(q_TLS_client_method());
- // Note, we assert, not just silently return/bail out early:
- // this should never happen and problems with OpenSSL's initialization
- // must be caught before this (see supportsSsl()).
- Q_ASSERT(myCtx);
- SSL *mySsl = q_SSL_new(myCtx);
- Q_ASSERT(mySsl);
-
- QList<QSslCipher> ciphers;
- QList<QSslCipher> defaultCiphers;
-
- q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
-
- q_SSL_CTX_free(myCtx);
- q_SSL_free(mySsl);
-
- setDefaultSupportedCiphers(ciphers);
- setDefaultCiphers(defaultCiphers);
-
-#if QT_CONFIG(dtls)
- ciphers.clear();
- defaultCiphers.clear();
- myCtx = q_SSL_CTX_new(q_DTLS_client_method());
- if (myCtx) {
- mySsl = q_SSL_new(myCtx);
- if (mySsl) {
- q_loadCiphersForConnection(mySsl, ciphers, defaultCiphers);
- q_setDefaultDtlsCiphers(defaultCiphers);
- q_SSL_free(mySsl);
- }
- q_SSL_CTX_free(myCtx);
- }
-#endif // dtls
-}
-
-void QSslSocketPrivate::resetDefaultEllipticCurves()
-{
- QVector<QSslEllipticCurve> curves;
-
-#ifndef OPENSSL_NO_EC
- const size_t curveCount = q_EC_get_builtin_curves(nullptr, 0);
-
- QVarLengthArray<EC_builtin_curve> builtinCurves(static_cast<int>(curveCount));
-
- if (q_EC_get_builtin_curves(builtinCurves.data(), curveCount) == curveCount) {
- curves.reserve(int(curveCount));
- for (size_t i = 0; i < curveCount; ++i) {
- QSslEllipticCurve curve;
- curve.id = builtinCurves[int(i)].nid;
- curves.append(curve);
- }
- }
-#endif // OPENSSL_NO_EC
-
- // set the list of supported ECs, but not the list
- // of *default* ECs. OpenSSL doesn't like forcing an EC for the wrong
- // ciphersuite, so don't try it -- leave the empty list to mean
- // "the implementation will choose the most suitable one".
- setDefaultSupportedEllipticCurves(curves);
-}
-
-#ifndef Q_OS_DARWIN // Apple implementation in qsslsocket_mac_shared.cpp
-QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
-{
- ensureInitialized();
-#ifdef QSSLSOCKET_DEBUG
- QElapsedTimer timer;
- timer.start();
-#endif
- QList<QSslCertificate> systemCerts;
-#if defined(Q_OS_WIN)
- HCERTSTORE hSystemStore;
- hSystemStore = CertOpenSystemStoreW(0, L"ROOT");
- if (hSystemStore) {
- PCCERT_CONTEXT pc = nullptr;
- while (1) {
- pc = CertFindCertificateInStore(hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, pc);
- if (!pc)
- break;
- QByteArray der(reinterpret_cast<const char *>(pc->pbCertEncoded),
- static_cast<int>(pc->cbCertEncoded));
- QSslCertificate cert(der, QSsl::Der);
- systemCerts.append(cert);
- }
- CertCloseStore(hSystemStore, 0);
- }
-#elif defined(Q_OS_UNIX)
- QSet<QString> certFiles;
- QDir currentDir;
- QStringList nameFilters;
- QList<QByteArray> directories;
- QSsl::EncodingFormat platformEncodingFormat;
-# ifndef Q_OS_ANDROID
- directories = unixRootCertDirectories();
- nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt");
- platformEncodingFormat = QSsl::Pem;
-# else
- // Q_OS_ANDROID
- QByteArray ministroPath = qgetenv("MINISTRO_SSL_CERTS_PATH"); // Set by Ministro
- directories << ministroPath;
- nameFilters << QLatin1String("*.der");
- platformEncodingFormat = QSsl::Der;
-# ifndef Q_OS_ANDROID_EMBEDDED
- if (ministroPath.isEmpty()) {
- QList<QByteArray> certificateData = fetchSslCertificateData();
- for (int i = 0; i < certificateData.size(); ++i) {
- systemCerts.append(QSslCertificate::fromData(certificateData.at(i), QSsl::Der));
- }
- } else
-# endif //Q_OS_ANDROID_EMBEDDED
-# endif //Q_OS_ANDROID
- {
- currentDir.setNameFilters(nameFilters);
- for (int a = 0; a < directories.count(); a++) {
- currentDir.setPath(QLatin1String(directories.at(a)));
- QDirIterator it(currentDir);
- while (it.hasNext()) {
- it.next();
- // use canonical path here to not load the same certificate twice if symlinked
- certFiles.insert(it.fileInfo().canonicalFilePath());
- }
- }
- for (const QString& file : qAsConst(certFiles))
- systemCerts.append(QSslCertificate::fromPath(file, platformEncodingFormat));
-# ifndef Q_OS_ANDROID
- systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva
- systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss
-# endif
- }
-#endif
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
- qCDebug(lcSsl) << "imported " << systemCerts.count() << " certificates";
-#endif
-
- return systemCerts;
-}
-#endif // Q_OS_DARWIN
-
-void QSslSocketBackendPrivate::startClientEncryption()
-{
- if (!initSslContext()) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl()));
- return;
- }
-
- // Start connecting. This will place outgoing data in the BIO, so we
- // follow up with calling transmit().
- startHandshake();
- transmit();
-}
-
-void QSslSocketBackendPrivate::startServerEncryption()
-{
- if (!initSslContext()) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Unable to init SSL Context: %1").arg(getErrorsFromOpenSsl()));
- return;
- }
-
- // Start connecting. This will place outgoing data in the BIO, so we
- // follow up with calling transmit().
- startHandshake();
- transmit();
-}
-
-/*!
- \internal
-
- Transmits encrypted data between the BIOs and the socket.
-*/
-void QSslSocketBackendPrivate::transmit()
-{
- Q_Q(QSslSocket);
-
- using ScopedBool = QScopedValueRollback<bool>;
-
- if (inSetAndEmitError)
- return;
-
- // If we don't have any SSL context, don't bother transmitting.
- if (!ssl)
- return;
-
- bool transmitting;
- do {
- transmitting = false;
-
- // If the connection is secure, we can transfer data from the write
- // buffer (in plain text) to the write BIO through SSL_write.
- if (connectionEncrypted && !writeBuffer.isEmpty()) {
- qint64 totalBytesWritten = 0;
- int nextDataBlockSize;
- while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
- int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize);
- if (writtenBytes <= 0) {
- int error = q_SSL_get_error(ssl, writtenBytes);
- //write can result in a want_write_error - not an error - continue transmitting
- if (error == SSL_ERROR_WANT_WRITE) {
- transmitting = true;
- break;
- } else if (error == SSL_ERROR_WANT_READ) {
- //write can result in a want_read error, possibly due to renegotiation - not an error - stop transmitting
- transmitting = false;
- break;
- } else {
- // ### Better error handling.
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Unable to write data: %1").arg(
- getErrorsFromOpenSsl()));
- return;
- }
- }
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: encrypted" << writtenBytes << "bytes";
-#endif
- writeBuffer.free(writtenBytes);
- totalBytesWritten += writtenBytes;
-
- if (writtenBytes < nextDataBlockSize) {
- // break out of the writing loop and try again after we had read
- transmitting = true;
- break;
- }
- }
-
- if (totalBytesWritten > 0) {
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- emittedBytesWritten = true;
- emit q->bytesWritten(totalBytesWritten);
- emittedBytesWritten = false;
- }
- emit q->channelBytesWritten(0, totalBytesWritten);
- }
- }
-
- // Check if we've got any data to be written to the socket.
- QVarLengthArray<char, 4096> data;
- int pendingBytes;
- while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0
- && plainSocket->openMode() != QIODevice::NotOpen) {
- // Read encrypted data from the write BIO into a buffer.
- data.resize(pendingBytes);
- int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes);
-
- // Write encrypted data from the buffer to the socket.
- qint64 actualWritten = plainSocket->write(data.constData(), encryptedBytesRead);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket" << actualWritten << "actual.";
-#endif
- if (actualWritten < 0) {
- //plain socket write fails if it was in the pending close state.
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
- return;
- }
- transmitting = true;
- }
-
- // Check if we've got any data to be read from the socket.
- if (!connectionEncrypted || !readBufferMaxSize || buffer.size() < readBufferMaxSize)
- while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
- // Read encrypted data from the socket into a buffer.
- data.resize(pendingBytes);
- // just peek() here because q_BIO_write could write less data than expected
- int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes);
-
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket";
-#endif
- // Write encrypted data from the buffer into the read BIO.
- int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead);
-
- // Throw away the results.
- if (writtenToBio > 0) {
- plainSocket->skip(writtenToBio);
- } else {
- // ### Better error handling.
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Unable to decrypt data: %1").arg(
- getErrorsFromOpenSsl()));
- return;
- }
-
- transmitting = true;
- }
-
- // If the connection isn't secured yet, this is the time to retry the
- // connect / accept.
- if (!connectionEncrypted) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: testing encryption";
-#endif
- if (startHandshake()) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: encryption established";
-#endif
- connectionEncrypted = true;
- transmitting = true;
- } else if (plainSocket->state() != QAbstractSocket::ConnectedState) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: connection lost";
-#endif
- break;
- } else if (paused) {
- // just wait until the user continues
- return;
- } else {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: encryption not done yet";
-#endif
- }
- }
-
- // If the request is small and the remote host closes the transmission
- // after sending, there's a chance that startHandshake() will already
- // have triggered a shutdown.
- if (!ssl)
- continue;
-
- // We always read everything from the SSL decryption buffers, even if
- // we have a readBufferMaxSize. There's no point in leaving data there
- // just so that readBuffer.size() == readBufferMaxSize.
- int readBytes = 0;
- const int bytesToRead = 4096;
- do {
- if (readChannelCount == 0) {
- // The read buffer is deallocated, don't try resize or write to it.
- break;
- }
- // Don't use SSL_pending(). It's very unreliable.
- readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead);
- if (readBytes > 0) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes";
-#endif
- buffer.chop(bytesToRead - readBytes);
-
- if (readyReadEmittedPointer)
- *readyReadEmittedPointer = true;
- emit q->readyRead();
- emit q->channelReadyRead(0);
- transmitting = true;
- continue;
- }
- buffer.chop(bytesToRead);
-
- // Error.
- switch (q_SSL_get_error(ssl, readBytes)) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- // Out of data.
- break;
- case SSL_ERROR_ZERO_RETURN:
- // The remote host closed the connection.
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::transmit: remote disconnect";
-#endif
- shutdown = true; // the other side shut down, make sure we do not send shutdown ourselves
- {
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- QSslSocket::tr("The TLS/SSL connection has been closed"));
- }
- return;
- case SSL_ERROR_SYSCALL: // some IO error
- case SSL_ERROR_SSL: // error in the SSL library
- // we do not know exactly what the error is, nor whether we can recover from it,
- // so just return to prevent an endless loop in the outer "while" statement
- {
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
- }
- return;
- default:
- // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
- // BIO_s_connect() or BIO_s_accept(), which we do not call.
- // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
- // SSL_CTX_set_client_cert_cb(), which we do not call.
- // So this default case should never be triggered.
- {
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
- }
- break;
- }
- } while (ssl && readBytes > 0);
- } while (ssl && transmitting);
-}
-
-QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
-{
- QSslError error;
- switch (errorCode) {
- case X509_V_OK:
- // X509_V_OK is also reported if the peer had no certificate.
- break;
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
- error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break;
- case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
- error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break;
- case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
- error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break;
- case X509_V_ERR_CERT_SIGNATURE_FAILURE:
- error = QSslError(QSslError::CertificateSignatureFailed, cert); break;
- case X509_V_ERR_CERT_NOT_YET_VALID:
- error = QSslError(QSslError::CertificateNotYetValid, cert); break;
- case X509_V_ERR_CERT_HAS_EXPIRED:
- error = QSslError(QSslError::CertificateExpired, cert); break;
- case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- error = QSslError(QSslError::InvalidNotBeforeField, cert); break;
- case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- error = QSslError(QSslError::InvalidNotAfterField, cert); break;
- case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- error = QSslError(QSslError::SelfSignedCertificate, cert); break;
- case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break;
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
- error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break;
- case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
- error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break;
- case X509_V_ERR_CERT_REVOKED:
- error = QSslError(QSslError::CertificateRevoked, cert); break;
- case X509_V_ERR_INVALID_CA:
- error = QSslError(QSslError::InvalidCaCertificate, cert); break;
- case X509_V_ERR_PATH_LENGTH_EXCEEDED:
- error = QSslError(QSslError::PathLengthExceeded, cert); break;
- case X509_V_ERR_INVALID_PURPOSE:
- error = QSslError(QSslError::InvalidPurpose, cert); break;
- case X509_V_ERR_CERT_UNTRUSTED:
- error = QSslError(QSslError::CertificateUntrusted, cert); break;
- case X509_V_ERR_CERT_REJECTED:
- error = QSslError(QSslError::CertificateRejected, cert); break;
- default:
- error = QSslError(QSslError::UnspecifiedError, cert); break;
- }
- return error;
-}
-
-QString QSslSocketBackendPrivate::msgErrorsDuringHandshake()
-{
- return QSslSocket::tr("Error during SSL handshake: %1")
- .arg(QSslSocketBackendPrivate::getErrorsFromOpenSsl());
-}
-
-bool QSslSocketBackendPrivate::startHandshake()
-{
- Q_Q(QSslSocket);
-
- // Check if the connection has been established. Get all errors from the
- // verification stage.
-
- using ScopedBool = QScopedValueRollback<bool>;
-
- if (inSetAndEmitError)
- return false;
-
- QVector<QSslErrorEntry> lastErrors;
- q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, &lastErrors);
- int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
- q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, nullptr);
-
- if (!lastErrors.isEmpty())
- storePeerCertificates();
- for (const auto &currentError : qAsConst(lastErrors)) {
- emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code,
- configuration.peerCertificateChain.value(currentError.depth)));
- if (q->state() != QAbstractSocket::ConnectedState)
- break;
- }
-
- errorList << lastErrors;
-
- // Connection aborted during handshake phase.
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
-
- // Check if we're encrypted or not.
- if (result <= 0) {
- switch (q_SSL_get_error(ssl, result)) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- // The handshake is not yet complete.
- break;
- default:
- QString errorString = QSslSocketBackendPrivate::msgErrorsDuringHandshake();
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::startHandshake: error!" << errorString;
-#endif
- {
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, errorString);
- }
- q->abort();
- }
- return false;
- }
-
- // store peer certificate chain
- storePeerCertificates();
-
- // Start translating errors.
- QList<QSslError> errors;
-
- // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
- for (const QSslCertificate &cert : qAsConst(configuration.peerCertificateChain)) {
- if (QSslCertificatePrivate::isBlacklisted(cert)) {
- QSslError error(QSslError::CertificateBlacklisted, cert);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
-
- const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
-
-#if QT_CONFIG(ocsp)
- // For now it's always QSslSocket::SslClientMode - initSslContext() will bail out early,
- // if it's enabled in QSslSocket::SslServerMode. This can change.
- if (!configuration.peerCertificate.isNull() && configuration.ocspStaplingEnabled && doVerifyPeer) {
- if (!checkOcspStatus()) {
- if (ocspErrors.isEmpty()) {
- {
- const ScopedBool bg(inSetAndEmitError, true);
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, ocspErrorDescription);
- }
- q->abort();
- return false;
- }
-
- for (const QSslError &error : ocspErrors) {
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- }
-#endif // ocsp
-
- // Check the peer certificate itself. First try the subject's common name
- // (CN) as a wildcard, then try all alternate subject name DNS entries the
- // same way.
- if (!configuration.peerCertificate.isNull()) {
- // but only if we're a client connecting to a server
- // if we're the server, don't check CN
- if (mode == QSslSocket::SslClientMode) {
- QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
-
- if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
- // No matches in common names or alternate names.
- QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- } else {
- // No peer certificate presented. Report as error if the socket
- // expected one.
- if (doVerifyPeer) {
- QSslError error(QSslError::NoPeerCertificate);
- errors << error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
-
- // Translate errors from the error list into QSslErrors.
- errors.reserve(errors.size() + errorList.size());
- for (const auto &error : qAsConst(errorList))
- errors << _q_OpenSSL_to_QSslError(error.code, configuration.peerCertificateChain.value(error.depth));
-
- if (!errors.isEmpty()) {
- sslErrors = errors;
-
-#ifdef Q_OS_WIN
- //Skip this if not using system CAs, or if the SSL errors are configured in advance to be ignorable
- if (doVerifyPeer
- && s_loadRootCertsOnDemand
- && allowRootCertOnDemandLoading
- && !verifyErrorsHaveBeenIgnored()) {
- //Windows desktop versions starting from vista ship with minimal set of roots
- //and download on demand from the windows update server CA roots that are
- //trusted by MS.
- //However, this is only transparent if using WinINET - we have to trigger it
- //ourselves.
- QSslCertificate certToFetch;
- bool fetchCertificate = true;
- for (int i=0; i< sslErrors.count(); i++) {
- switch (sslErrors.at(i).error()) {
- case QSslError::UnableToGetLocalIssuerCertificate: // site presented intermediate cert, but root is unknown
- case QSslError::SelfSignedCertificateInChain: // site presented a complete chain, but root is unknown
- certToFetch = sslErrors.at(i).certificate();
- break;
- case QSslError::SelfSignedCertificate:
- case QSslError::CertificateBlacklisted:
- //With these errors, we know it will be untrusted so save time by not asking windows
- fetchCertificate = false;
- break;
- default:
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << sslErrors.at(i).errorString();
-#endif
- break;
- }
- }
- if (fetchCertificate && !certToFetch.isNull()) {
- fetchCaRootForCert(certToFetch);
- return false;
- }
- }
-#endif
- if (!checkSslErrors())
- return false;
- // A slot, attached to sslErrors signal can call
- // abort/close/disconnetFromHost/etc; no need to
- // continue handshake then.
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- } else {
- sslErrors.clear();
- }
-
- continueHandshake();
- return true;
-}
-
-void QSslSocketBackendPrivate::storePeerCertificates()
-{
- // Store the peer certificate and chain. For clients, the peer certificate
- // chain includes the peer certificate; for servers, it doesn't. Both the
- // peer certificate and the chain may be empty if the peer didn't present
- // any certificate.
- X509 *x509 = q_SSL_get_peer_certificate(ssl);
- configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
- q_X509_free(x509);
- if (configuration.peerCertificateChain.isEmpty()) {
- configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
- if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
- configuration.peerCertificateChain.prepend(configuration.peerCertificate);
- }
-}
-
-bool QSslSocketBackendPrivate::checkSslErrors()
-{
- Q_Q(QSslSocket);
- if (sslErrors.isEmpty())
- return true;
-
- emit q->sslErrors(sslErrors);
-
- bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
- bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
- // check whether we need to emit an SSL handshake error
- if (doVerifyPeer && doEmitSslError) {
- if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
- pauseSocketNotifiers(q);
- paused = true;
- } else {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
- plainSocket->disconnectFromHost();
- }
- return false;
- }
- return true;
-}
-
-unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint,
- char *identity, unsigned int max_identity_len,
- unsigned char *psk, unsigned int max_psk_len)
-{
- QSslPreSharedKeyAuthenticator authenticator;
-
- // Fill in some read-only fields (for the user)
- if (hint)
- authenticator.d->identityHint = QByteArray::fromRawData(hint, int(::strlen(hint))); // it's NUL terminated, but do not include the NUL
-
- authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NUL terminated
- authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
-
- // Let the client provide the remaining bits...
- Q_Q(QSslSocket);
- emit q->preSharedKeyAuthenticationRequired(&authenticator);
-
- // No PSK set? Return now to make the handshake fail
- if (authenticator.preSharedKey().isEmpty())
- return 0;
-
- // Copy data back into OpenSSL
- const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength());
- ::memcpy(identity, authenticator.identity().constData(), identityLength);
- identity[identityLength] = 0;
-
- const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
- ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
- return pskLength;
-}
-
-unsigned int QSslSocketBackendPrivate::tlsPskServerCallback(const char *identity,
- unsigned char *psk, unsigned int max_psk_len)
-{
- QSslPreSharedKeyAuthenticator authenticator;
-
- // Fill in some read-only fields (for the user)
- authenticator.d->identityHint = configuration.preSharedKeyIdentityHint;
- authenticator.d->identity = identity;
- authenticator.d->maximumIdentityLength = 0; // user cannot set an identity
- authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
-
- // Let the client provide the remaining bits...
- Q_Q(QSslSocket);
- emit q->preSharedKeyAuthenticationRequired(&authenticator);
-
- // No PSK set? Return now to make the handshake fail
- if (authenticator.preSharedKey().isEmpty())
- return 0;
-
- // Copy data back into OpenSSL
- const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
- ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
- return pskLength;
-}
-
-#ifdef Q_OS_WIN
-
-void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert)
-{
- Q_Q(QSslSocket);
- //The root certificate is downloaded from windows update, which blocks for 15 seconds in the worst case
- //so the request is done in a worker thread.
- QWindowsCaRootFetcher *fetcher = new QWindowsCaRootFetcher(cert, mode);
- QObject::connect(fetcher, SIGNAL(finished(QSslCertificate,QSslCertificate)), q, SLOT(_q_caRootLoaded(QSslCertificate,QSslCertificate)), Qt::QueuedConnection);
- QMetaObject::invokeMethod(fetcher, "start", Qt::QueuedConnection);
- pauseSocketNotifiers(q);
- paused = true;
-}
-
-//This is the callback from QWindowsCaRootFetcher, trustedRoot will be invalid (default constructed) if it failed.
-void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertificate trustedRoot)
-{
- Q_Q(QSslSocket);
- if (!trustedRoot.isNull() && !trustedRoot.isBlacklisted()) {
- if (s_loadRootCertsOnDemand) {
- //Add the new root cert to default cert list for use by future sockets
- QSslSocket::addDefaultCaCertificate(trustedRoot);
- }
- //Add the new root cert to this socket for future connections
- q->addCaCertificate(trustedRoot);
- //Remove the broken chain ssl errors (as chain is verified by windows)
- for (int i=sslErrors.count() - 1; i >= 0; --i) {
- if (sslErrors.at(i).certificate() == cert) {
- switch (sslErrors.at(i).error()) {
- case QSslError::UnableToGetLocalIssuerCertificate:
- case QSslError::CertificateUntrusted:
- case QSslError::UnableToVerifyFirstCertificate:
- case QSslError::SelfSignedCertificateInChain:
- // error can be ignored if OS says the chain is trusted
- sslErrors.removeAt(i);
- break;
- default:
- // error cannot be ignored
- break;
- }
- }
- }
- }
- // Continue with remaining errors
- if (plainSocket)
- plainSocket->resume();
- paused = false;
- if (checkSslErrors() && ssl) {
- bool willClose = (autoStartHandshake && pendingClose);
- continueHandshake();
- if (!willClose)
- transmit();
- }
-}
-
-#endif
-
-#if QT_CONFIG(ocsp)
-
-bool QSslSocketBackendPrivate::checkOcspStatus()
-{
- Q_ASSERT(ssl);
- Q_ASSERT(mode == QSslSocket::SslClientMode); // See initSslContext() for SslServerMode
- Q_ASSERT(configuration.peerVerifyMode != QSslSocket::VerifyNone);
-
- ocspResponses.clear();
- ocspErrorDescription.clear();
- ocspErrors.clear();
-
- const unsigned char *responseData = nullptr;
- const long responseLength = q_SSL_get_tlsext_status_ocsp_resp(ssl, &responseData);
- if (responseLength <= 0 || !responseData) {
- ocspErrors.push_back(QSslError::OcspNoResponseFound);
- return false;
- }
-
- OCSP_RESPONSE *response = q_d2i_OCSP_RESPONSE(nullptr, &responseData, responseLength);
- if (!response) {
- // Treat this as a fatal SslHandshakeError.
- ocspErrorDescription = QSslSocket::tr("Failed to decode OCSP response");
- return false;
- }
- const QSharedPointer<OCSP_RESPONSE> responseGuard(response, q_OCSP_RESPONSE_free);
-
- const int ocspStatus = q_OCSP_response_status(response);
- if (ocspStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
- // It's not a definitive response, it's an error message (not signed by the responder).
- ocspErrors.push_back(qt_OCSP_response_status_to_QSslError(ocspStatus));
- return false;
- }
-
- OCSP_BASICRESP *basicResponse = q_OCSP_response_get1_basic(response);
- if (!basicResponse) {
- // SslHandshakeError.
- ocspErrorDescription = QSslSocket::tr("Failed to extract basic OCSP response");
- return false;
- }
- const QSharedPointer<OCSP_BASICRESP> basicResponseGuard(basicResponse, q_OCSP_BASICRESP_free);
-
- SSL_CTX *ctx = q_SSL_get_SSL_CTX(ssl); // Does not increment refcount.
- Q_ASSERT(ctx);
- X509_STORE *store = q_SSL_CTX_get_cert_store(ctx); // Does not increment refcount.
- if (!store) {
- // SslHandshakeError.
- ocspErrorDescription = QSslSocket::tr("No certificate verification store, cannot verify OCSP response");
- return false;
- }
-
- STACK_OF(X509) *peerChain = q_SSL_get_peer_cert_chain(ssl); // Does not increment refcount.
- X509 *peerX509 = q_SSL_get_peer_certificate(ssl);
- Q_ASSERT(peerChain || peerX509);
- const QSharedPointer<X509> peerX509Guard(peerX509, q_X509_free);
- // OCSP_basic_verify with 0 as verificationFlags:
- //
- // 0) Tries to find the OCSP responder's certificate in either peerChain
- // or basicResponse->certs. If not found, verification fails.
- // 1) It checks the signature using the responder's public key.
- // 2) Then it tries to validate the responder's cert (building a chain
- // etc.)
- // 3) It checks CertID in response.
- // 4) Ensures the responder is authorized to sign the status respond.
- //
- // Note, OpenSSL prior to 1.0.2b would only use bs->certs to
- // verify the responder's chain (see their commit 4ba9a4265bd).
- // Working this around - is too much fuss for ancient versions we
- // are dropping quite soon anyway.
- const unsigned long verificationFlags = 0;
- const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
- if (success <= 0)
- ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted);
-
- if (q_OCSP_resp_count(basicResponse) != 1) {
- ocspErrors.push_back(QSslError::OcspMalformedResponse);
- return false;
- }
-
- OCSP_SINGLERESP *singleResponse = q_OCSP_resp_get0(basicResponse, 0);
- if (!singleResponse) {
- ocspErrors.clear();
- // A fatal problem -> SslHandshakeError.
- ocspErrorDescription = QSslSocket::tr("Failed to decode a SingleResponse from OCSP status response");
- return false;
- }
-
- // Let's make sure the response is for the correct certificate - we
- // can re-create this CertID using our peer's certificate and its
- // issuer's public key.
- ocspResponses.push_back(QOcspResponse());
- QOcspResponsePrivate *dResponse = ocspResponses.back().d.data();
- dResponse->subjectCert = configuration.peerCertificate;
- bool matchFound = false;
- if (configuration.peerCertificate.isSelfSigned()) {
- dResponse->signerCert = configuration.peerCertificate;
- matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, peerX509);
- } else {
- const STACK_OF(X509) *certs = q_SSL_get_peer_cert_chain(ssl);
- if (!certs) // Oh, what a cataclysm! Last try:
- certs = q_OCSP_resp_get0_certs(basicResponse);
- if (certs) {
- // It could be the first certificate in 'certs' is our peer's
- // certificate. Since it was not captured by the 'self-signed' branch
- // above, the CertID will not match and we'll just iterate on to the
- // next certificate. So we start from 0, not 1.
- for (int i = 0, e = q_sk_X509_num(certs); i < e; ++i) {
- X509 *issuer = q_sk_X509_value(certs, i);
- matchFound = qt_OCSP_certificate_match(singleResponse, peerX509, issuer);
- if (matchFound) {
- if (q_X509_check_issued(issuer, peerX509) == X509_V_OK) {
- dResponse->signerCert = QSslCertificatePrivate::QSslCertificate_from_X509(issuer);
- break;
- }
- matchFound = false;
- }
- }
- }
- }
-
- if (!matchFound) {
- dResponse->signerCert.clear();
- ocspErrors.push_back({QSslError::OcspResponseCertIdUnknown, configuration.peerCertificate});
- }
-
- // Check if the response is valid time-wise:
- ASN1_GENERALIZEDTIME *revTime = nullptr;
- ASN1_GENERALIZEDTIME *thisUpdate = nullptr;
- ASN1_GENERALIZEDTIME *nextUpdate = nullptr;
- int reason;
- const int certStatus = q_OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate);
- if (!thisUpdate) {
- // This is unexpected, treat as SslHandshakeError, OCSP_check_validity assumes this pointer
- // to be != nullptr.
- ocspErrors.clear();
- ocspResponses.clear();
- ocspErrorDescription = QSslSocket::tr("Failed to extract 'this update time' from the SingleResponse");
- return false;
- }
-
- // OCSP_check_validity(this, next, nsec, maxsec) does this check:
- // this <= now <= next. They allow some freedom to account
- // for delays/time inaccuracy.
- // this > now + nsec ? -> NOT_YET_VALID
- // if maxsec >= 0:
- // now - maxsec > this ? -> TOO_OLD
- // now - nsec > next ? -> EXPIRED
- // next < this ? -> NEXT_BEFORE_THIS
- // OK.
- if (!q_OCSP_check_validity(thisUpdate, nextUpdate, 60, -1))
- ocspErrors.push_back({QSslError::OcspResponseExpired, configuration.peerCertificate});
-
- // And finally, the status:
- switch (certStatus) {
- case V_OCSP_CERTSTATUS_GOOD:
- // This certificate was not found among the revoked ones.
- dResponse->certificateStatus = QOcspCertificateStatus::Good;
- break;
- case V_OCSP_CERTSTATUS_REVOKED:
- dResponse->certificateStatus = QOcspCertificateStatus::Revoked;
- dResponse->revocationReason = qt_OCSP_revocation_reason(reason);
- ocspErrors.push_back({QSslError::CertificateRevoked, configuration.peerCertificate});
- break;
- case V_OCSP_CERTSTATUS_UNKNOWN:
- dResponse->certificateStatus = QOcspCertificateStatus::Unknown;
- ocspErrors.push_back({QSslError::OcspStatusUnknown, configuration.peerCertificate});
- }
-
- return !ocspErrors.size();
-}
-
-#endif // ocsp
-
-void QSslSocketBackendPrivate::disconnectFromHost()
-{
- if (ssl) {
- if (!shutdown) {
- q_SSL_shutdown(ssl);
- shutdown = true;
- transmit();
- }
- }
- plainSocket->disconnectFromHost();
-}
-
-void QSslSocketBackendPrivate::disconnected()
-{
- if (plainSocket->bytesAvailable() <= 0)
- destroySslContext();
- else {
- // Move all bytes into the plain buffer
- qint64 tmpReadBufferMaxSize = readBufferMaxSize;
- readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained
- transmit();
- readBufferMaxSize = tmpReadBufferMaxSize;
- }
- //if there is still buffered data in the plain socket, don't destroy the ssl context yet.
- //it will be destroyed when the socket is deleted.
-}
-
-QSslCipher QSslSocketBackendPrivate::sessionCipher() const
-{
- if (!ssl)
- return QSslCipher();
-
- const SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl);
- return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher();
-}
-
-QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
-{
- if (!ssl)
- return QSsl::UnknownProtocol;
- int ver = q_SSL_version(ssl);
-
- switch (ver) {
- case 0x2:
- return QSsl::SslV2;
- case 0x300:
- return QSsl::SslV3;
- case 0x301:
- return QSsl::TlsV1_0;
- case 0x302:
- return QSsl::TlsV1_1;
- case 0x303:
- return QSsl::TlsV1_2;
- case 0x304:
- return QSsl::TlsV1_3;
- }
-
- return QSsl::UnknownProtocol;
-}
-
-
-void QSslSocketBackendPrivate::continueHandshake()
-{
- Q_Q(QSslSocket);
- // if we have a max read buffer size, reset the plain socket's to match
- if (readBufferMaxSize)
- plainSocket->setReadBufferSize(readBufferMaxSize);
-
- if (q_SSL_session_reused(ssl))
- configuration.peerSessionShared = true;
-
-#ifdef QT_DECRYPT_SSL_TRAFFIC
- if (q_SSL_get_session(ssl)) {
- size_t master_key_len = q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl), 0, 0);
- size_t client_random_len = q_SSL_get_client_random(ssl, 0, 0);
- QByteArray masterKey(int(master_key_len), 0); // Will not overflow
- QByteArray clientRandom(int(client_random_len), 0); // Will not overflow
-
- q_SSL_SESSION_get_master_key(q_SSL_get_session(ssl),
- reinterpret_cast<unsigned char*>(masterKey.data()),
- masterKey.size());
- q_SSL_get_client_random(ssl, reinterpret_cast<unsigned char *>(clientRandom.data()),
- clientRandom.size());
-
- QByteArray debugLineClientRandom("CLIENT_RANDOM ");
- debugLineClientRandom.append(clientRandom.toHex().toUpper());
- debugLineClientRandom.append(" ");
- debugLineClientRandom.append(masterKey.toHex().toUpper());
- debugLineClientRandom.append("\n");
-
- QString sslKeyFile = QDir::tempPath() + QLatin1String("/qt-ssl-keys");
- QFile file(sslKeyFile);
- if (!file.open(QIODevice::Append))
- qCWarning(lcSsl) << "could not open file" << sslKeyFile << "for appending";
- if (!file.write(debugLineClientRandom))
- qCWarning(lcSsl) << "could not write to file" << sslKeyFile;
- file.close();
- } else {
- qCWarning(lcSsl, "could not decrypt SSL traffic");
- }
-#endif
-
- // Cache this SSL session inside the QSslContext
- if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionSharing)) {
- if (!sslContextPointer->cacheSession(ssl)) {
- sslContextPointer.clear(); // we could not cache the session
- } else {
- // Cache the session for permanent usage as well
- if (!(configuration.sslOptions & QSsl::SslOptionDisableSessionPersistence)) {
- if (!sslContextPointer->sessionASN1().isEmpty())
- configuration.sslSession = sslContextPointer->sessionASN1();
- configuration.sslSessionTicketLifeTimeHint = sslContextPointer->sessionTicketLifeTimeHint();
- }
- }
- }
-
-#if !defined(OPENSSL_NO_NEXTPROTONEG)
-
- configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
- if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) {
- // we could not agree -> be conservative and use HTTP/1.1
- configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1");
- } else {
- const unsigned char *proto = nullptr;
- unsigned int proto_len = 0;
-
- q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
- if (proto_len && mode == QSslSocket::SslClientMode) {
- // Client does not have a callback that sets it ...
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
- }
-
- if (!proto_len) { // Test if NPN was more lucky ...
- q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
- }
-
- if (proto_len)
- configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
- else
- configuration.nextNegotiatedProtocol.clear();
- }
-#endif // !defined(OPENSSL_NO_NEXTPROTONEG)
-
- if (mode == QSslSocket::SslClientMode) {
- EVP_PKEY *key;
- if (q_SSL_get_server_tmp_key(ssl, &key))
- configuration.ephemeralServerKey = QSslKey(key, QSsl::PublicKey);
- }
-
- connectionEncrypted = true;
- emit q->encrypted();
- if (autoStartHandshake && pendingClose) {
- pendingClose = false;
- q->disconnectFromHost();
- }
-}
-
-bool QSslSocketPrivate::ensureLibraryLoaded()
-{
- if (!q_resolveOpenSslSymbols())
- return false;
-
- const QMutexLocker locker(qt_opensslInitMutex);
-
- if (!s_libraryLoaded) {
- // Initialize OpenSSL.
- if (q_OPENSSL_init_ssl(0, nullptr) != 1)
- return false;
- q_SSL_load_error_strings();
- q_OpenSSL_add_all_algorithms();
-
- QSslSocketBackendPrivate::s_indexForSSLExtraData
- = q_CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0L, nullptr, nullptr,
- nullptr, nullptr);
-
- // Initialize OpenSSL's random seed.
- if (!q_RAND_status()) {
- qWarning("Random number generator not seeded, disabling SSL support");
- return false;
- }
-
- s_libraryLoaded = true;
- }
- return true;
-}
-
-void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
-{
- const QMutexLocker locker(qt_opensslInitMutex);
-
- if (s_loadedCiphersAndCerts)
- return;
- s_loadedCiphersAndCerts = true;
-
- resetDefaultCiphers();
- resetDefaultEllipticCurves();
-
-#if QT_CONFIG(library)
- //load symbols needed to receive certificates from system store
-#if defined(Q_OS_QNX)
- s_loadRootCertsOnDemand = true;
-#elif defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
- // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
- QList<QByteArray> dirs = unixRootCertDirectories();
- QStringList symLinkFilter;
- symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]");
- for (int a = 0; a < dirs.count(); ++a) {
- QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files);
- if (iterator.hasNext()) {
- s_loadRootCertsOnDemand = true;
- break;
- }
- }
-#endif
-#endif // QT_CONFIG(library)
- // if on-demand loading was not enabled, load the certs now
- if (!s_loadRootCertsOnDemand)
- setDefaultCaCertificates(systemCaCertificates());
-#ifdef Q_OS_WIN
- //Enabled for fetching additional root certs from windows update on windows.
- //This flag is set false by setDefaultCaCertificates() indicating the app uses
- //its own cert bundle rather than the system one.
- //Same logic that disables the unix on demand cert loading.
- //Unlike unix, we do preload the certificates from the cert store.
- s_loadRootCertsOnDemand = true;
-#endif
-}
-
-QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509)
-{
- ensureInitialized();
- QList<QSslCertificate> certificates;
- for (int i = 0; i < q_sk_X509_num(x509); ++i) {
- if (X509 *entry = q_sk_X509_value(x509, i))
- certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry);
- }
- return certificates;
-}
-
-QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
-{
- QList<QSslError> errors;
- if (certificateChain.count() <= 0) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Setup the store with the default CA certificates
- X509_STORE *certStore = q_X509_STORE_new();
- if (!certStore) {
- qCWarning(lcSsl) << "Unable to create certificate store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- const std::unique_ptr<X509_STORE, decltype(&q_X509_STORE_free)> storeGuard(certStore, q_X509_STORE_free);
-
- if (s_loadRootCertsOnDemand) {
- setDefaultCaCertificates(defaultCaCertificates() + systemCaCertificates());
- }
-
- const QDateTime now = QDateTime::currentDateTimeUtc();
- const auto caCertificates = QSslConfiguration::defaultConfiguration().caCertificates();
- for (const QSslCertificate &caCertificate : caCertificates) {
- // From https://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html:
- //
- // If several CA certificates matching the name, key identifier, and
- // serial number condition are available, only the first one will be
- // examined. This may lead to unexpected results if the same CA
- // certificate is available with different expiration dates. If a
- // ``certificate expired'' verification error occurs, no other
- // certificate will be searched. Make sure to not have expired
- // certificates mixed with valid ones.
- //
- // See also: QSslContext::fromConfiguration()
- if (caCertificate.expiryDate() >= now) {
- q_X509_STORE_add_cert(certStore, reinterpret_cast<X509 *>(caCertificate.handle()));
- }
- }
-
- QVector<QSslErrorEntry> lastErrors;
- if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
- qCWarning(lcSsl) << "Unable to attach external data (error list) to a store";
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Register a custom callback to get all verification errors.
- q_X509_STORE_set_verify_cb(certStore, q_X509Callback);
-
- // Build the chain of intermediate certificates
- STACK_OF(X509) *intermediates = nullptr;
- if (certificateChain.length() > 1) {
- intermediates = (STACK_OF(X509) *) q_OPENSSL_sk_new_null();
-
- if (!intermediates) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- bool first = true;
- for (const QSslCertificate &cert : certificateChain) {
- if (first) {
- first = false;
- continue;
- }
-
- q_OPENSSL_sk_push((OPENSSL_STACK *)intermediates, reinterpret_cast<X509 *>(cert.handle()));
- }
- }
-
- X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new();
- if (!storeContext) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
- std::unique_ptr<X509_STORE_CTX, decltype(&q_X509_STORE_CTX_free)> ctxGuard(storeContext, q_X509_STORE_CTX_free);
-
- if (!q_X509_STORE_CTX_init(storeContext, certStore, reinterpret_cast<X509 *>(certificateChain[0].handle()), intermediates)) {
- errors << QSslError(QSslError::UnspecifiedError);
- return errors;
- }
-
- // Now we can actually perform the verification of the chain we have built.
- // We ignore the result of this function since we process errors via the
- // callback.
- (void) q_X509_verify_cert(storeContext);
- ctxGuard.reset();
- q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
-
- // Now process the errors
-
- if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) {
- QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]);
- errors << error;
- }
-
- // Check the certificate name against the hostname if one was specified
- if ((!hostName.isEmpty()) && (!isMatchingHostname(certificateChain[0], hostName))) {
- // No matches in common names or alternate names.
- QSslError error(QSslError::HostNameMismatch, certificateChain[0]);
- errors << error;
- }
-
- // Translate errors from the error list into QSslErrors.
- errors.reserve(errors.size() + lastErrors.size());
- for (const auto &error : qAsConst(lastErrors))
- errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth));
-
- return errors;
-}
-
-bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase)
-{
- if (!supportsSsl())
- return false;
-
- // These are required
- Q_ASSERT(device);
- Q_ASSERT(key);
- Q_ASSERT(cert);
-
- // Read the file into a BIO
- QByteArray pkcs12data = device->readAll();
- if (pkcs12data.size() == 0)
- return false;
-
- BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pkcs12data.constData()), pkcs12data.size());
-
- // Create the PKCS#12 object
- PKCS12 *p12 = q_d2i_PKCS12_bio(bio, nullptr);
- if (!p12) {
- qCWarning(lcSsl, "Unable to read PKCS#12 structure, %s",
- q_ERR_error_string(q_ERR_get_error(), nullptr));
- q_BIO_free(bio);
- return false;
- }
-
- // Extract the data
- EVP_PKEY *pkey = nullptr;
- X509 *x509;
- STACK_OF(X509) *ca = nullptr;
-
- if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) {
- qCWarning(lcSsl, "Unable to parse PKCS#12 structure, %s",
- q_ERR_error_string(q_ERR_get_error(), nullptr));
- q_PKCS12_free(p12);
- q_BIO_free(bio);
- return false;
- }
-
- // Convert to Qt types
- if (!key->d->fromEVP_PKEY(pkey)) {
- qCWarning(lcSsl, "Unable to convert private key");
- q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
- reinterpret_cast<void (*)(void *)>(q_X509_free));
- q_X509_free(x509);
- q_EVP_PKEY_free(pkey);
- q_PKCS12_free(p12);
- q_BIO_free(bio);
-
- return false;
- }
-
- *cert = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
-
- if (caCertificates)
- *caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca);
-
- // Clean up
- q_OPENSSL_sk_pop_free(reinterpret_cast<OPENSSL_STACK *>(ca),
- reinterpret_cast<void (*)(void *)>(q_X509_free));
-
- q_X509_free(x509);
- q_EVP_PKEY_free(pkey);
- q_PKCS12_free(p12);
- q_BIO_free(bio);
-
- return true;
-}
-
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_android.cpp b/src/network/ssl/qsslsocket_openssl_android.cpp
deleted file mode 100644
index b5d2458d56..0000000000
--- a/src/network/ssl/qsslsocket_openssl_android.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-#include "qsslsocket_openssl_p.h"
-#include <QtCore/private/qjni_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData()
-{
- QList<QByteArray> certificateData;
-
- QJNIObjectPrivate certificates = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
- "getSSLCertificates",
- "()[[B");
- if (!certificates.isValid())
- return certificateData;
-
- QJNIEnvironmentPrivate env;
- jobjectArray jcertificates = static_cast<jobjectArray>(certificates.object());
- const jint nCertificates = env->GetArrayLength(jcertificates);
- certificateData.reserve(static_cast<int>(nCertificates));
-
- for (int i = 0; i < nCertificates; ++i) {
- jbyteArray jCert = static_cast<jbyteArray>(env->GetObjectArrayElement(jcertificates, i));
- const uint sz = env->GetArrayLength(jCert);
- jbyte *buffer = env->GetByteArrayElements(jCert, 0);
- certificateData.append(QByteArray(reinterpret_cast<char*>(buffer), sz));
-
- env->ReleaseByteArrayElements(jCert, buffer, JNI_ABORT); // don't copy back the elements
- env->DeleteLocalRef(jCert);
- }
-
- return certificateData;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
deleted file mode 100644
index 0370a7d2ac..0000000000
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_OPENSSL_P_H
-#define QSSLSOCKET_OPENSSL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qsslsocket_p.h"
-
-#include <QtCore/qvector.h>
-#include <QtCore/qstring.h>
-
-#ifdef Q_OS_WIN
-#include <qt_windows.h>
-#if defined(OCSP_RESPONSE)
-#undef OCSP_RESPONSE
-#endif
-#if defined(X509_NAME)
-#undef X509_NAME
-#endif
-#endif // Q_OS_WIN
-
-#include <openssl/asn1.h>
-#include <openssl/bio.h>
-#include <openssl/bn.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs12.h>
-#include <openssl/pkcs7.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-#include <openssl/stack.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
-#include <openssl/x509_vfy.h>
-#include <openssl/dsa.h>
-#include <openssl/rsa.h>
-#include <openssl/crypto.h>
-#include <openssl/tls1.h>
-
-#if QT_CONFIG(opensslv11)
-#include <openssl/dh.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-struct QSslErrorEntry {
- int code;
- int depth;
-
- static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx);
-};
-Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE);
-
-class QSslSocketBackendPrivate : public QSslSocketPrivate
-{
- Q_DECLARE_PUBLIC(QSslSocket)
-public:
- QSslSocketBackendPrivate();
- virtual ~QSslSocketBackendPrivate();
-
- // SSL context
- bool initSslContext();
- void destroySslContext();
- SSL *ssl;
- BIO *readBio;
- BIO *writeBio;
- SSL_SESSION *session;
- QVector<QSslErrorEntry> errorList;
- static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate
-
- bool inSetAndEmitError = false;
-
- // Platform specific functions
- void startClientEncryption() override;
- void startServerEncryption() override;
- void transmit() override;
- bool startHandshake();
- void disconnectFromHost() override;
- void disconnected() override;
- QSslCipher sessionCipher() const override;
- QSsl::SslProtocol sessionProtocol() const override;
- void continueHandshake() override;
- bool checkSslErrors();
- void storePeerCertificates();
- unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
- unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len);
-#ifdef Q_OS_WIN
- void fetchCaRootForCert(const QSslCertificate &cert);
- void _q_caRootLoaded(QSslCertificate,QSslCertificate) override;
-#endif
-
-#if QT_CONFIG(ocsp)
- bool checkOcspStatus();
-#endif
-
- // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
- QString ocspErrorDescription;
- // These will go to sslErrors()
- QVector<QSslError> ocspErrors;
- QByteArray ocspResponseDer;
-
- Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
- static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
- static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
- static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
- static QString getErrorsFromOpenSsl();
- static bool importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase);
-
- static QString msgErrorsDuringHandshake();
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
deleted file mode 100644
index 3504924888..0000000000
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ /dev/null
@@ -1,1275 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-#include "qssl_p.h"
-#include "qsslsocket_openssl_symbols_p.h"
-
-#ifdef Q_OS_WIN
-# include <private/qsystemlibrary_p.h>
-#elif QT_CONFIG(library)
-# include <QtCore/qlibrary.h>
-#endif
-#include <QtCore/qmutex.h>
-#include <QtCore/qdatetime.h>
-#if defined(Q_OS_UNIX)
-#include <QtCore/qdir.h>
-#endif
-#include <QtCore/private/qmemory_p.h>
-#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
-#include <link.h>
-#endif
-#ifdef Q_OS_DARWIN
-#include "private/qcore_mac_p.h"
-#endif
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-/*
- Note to maintainer:
- -------------------
-
- We load OpenSSL symbols dynamically. Because symbols are known to
- disappear, and signatures sometimes change, between releases, we need to
- be careful about how this is done. To ensure we don't end up dereferencing
- null function pointers, and continue running even if certain functions are
- missing, we define helper functions for each of the symbols we load from
- OpenSSL, all prefixed with "q_" (declared in
- qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect
- directly, we call q_SSL_connect, which is a function that checks if the
- actual SSL_connect fptr is null, and returns a failure if it is, or calls
- SSL_connect if it isn't.
-
- This requires a somewhat tedious process of declaring each function we
- want to call in OpenSSL thrice: once with the q_, in _p.h, once using the
- DEFINEFUNC macros below, and once in the function that actually resolves
- the symbols, below the DEFINEFUNC declarations below.
-
- There's one DEFINEFUNC macro declared for every number of arguments
- exposed by OpenSSL (feel free to extend when needed). The easiest thing to
- do is to find an existing entry that matches the arg count of the function
- you want to import, and do the same.
-
- The first macro arg is the function return type. The second is the
- verbatim name of the function/symbol. Then follows a list of N pairs of
- argument types with a variable name, and just the variable name (char *a,
- a, char *b, b, etc). Finally there's two arguments - a suitable return
- statement for the error case (for an int function, return 0 or return -1
- is usually right). Then either just "return" or DUMMYARG, the latter being
- for void functions.
-
- Note: Take into account that these macros and declarations are processed
- at compile-time, and the result depends on the OpenSSL headers the
- compiling host has installed, but the symbols are resolved at run-time,
- possibly with a different version of OpenSSL.
-*/
-
-#ifndef QT_LINKED_OPENSSL
-
-namespace {
-void qsslSocketUnresolvedSymbolWarning(const char *functionName)
-{
- qCWarning(lcSsl, "QSslSocket: cannot call unresolved function %s", functionName);
-}
-
-#if QT_CONFIG(library)
-void qsslSocketCannotResolveSymbolWarning(const char *functionName)
-{
- qCWarning(lcSsl, "QSslSocket: cannot resolve %s", functionName);
-}
-#endif
-
-}
-
-#endif // QT_LINKED_OPENSSL
-
-DEFINEFUNC(const unsigned char *, ASN1_STRING_get0_data, const ASN1_STRING *a, a, return nullptr, return)
-DEFINEFUNC2(int, OPENSSL_init_ssl, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
-DEFINEFUNC2(int, OPENSSL_init_crypto, uint64_t opts, opts, const OPENSSL_INIT_SETTINGS *settings, settings, return 0, return)
-DEFINEFUNC(BIO *, BIO_new, const BIO_METHOD *a, a, return nullptr, return)
-DEFINEFUNC(const BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
-DEFINEFUNC(int, EVP_CIPHER_CTX_reset, EVP_CIPHER_CTX *c, c, return 0, return)
-DEFINEFUNC(int, EVP_PKEY_up_ref, EVP_PKEY *a, a, return 0, return)
-DEFINEFUNC(int, EVP_PKEY_base_id, EVP_PKEY *a, a, return NID_undef, return)
-DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
-DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
-DEFINEFUNC(int, OPENSSL_sk_num, OPENSSL_STACK *a, a, return -1, return)
-DEFINEFUNC2(void, OPENSSL_sk_pop_free, OPENSSL_STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
-DEFINEFUNC(OPENSSL_STACK *, OPENSSL_sk_new_null, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(void, OPENSSL_sk_push, OPENSSL_STACK *a, a, void *b, b, return, DUMMYARG)
-DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
-DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
-DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
-DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
-#ifdef TLS1_3_VERSION
-DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
-DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG)
-#endif
-DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
-DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
-DEFINEFUNC6(int, CRYPTO_get_ex_new_index, int class_index, class_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
-DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op, return 0, return)
-
-DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
-DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
-DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
-DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
-DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
-DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
-DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *data, data, return 0, return)
-DEFINEFUNC2(void *, X509_STORE_get_ex_data, X509_STORE *r, r, int idx, idx, return nullptr, return)
-DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return)
-DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG)
-DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return)
-DEFINEFUNC(const char *, OpenSSL_version, int a, a, return nullptr, return)
-DEFINEFUNC(unsigned long, SSL_SESSION_get_ticket_lifetime_hint, const SSL_SESSION *session, session, return 0, return)
-DEFINEFUNC4(void, DH_get0_pqg, const DH *dh, dh, const BIGNUM **p, p, const BIGNUM **q, q, const BIGNUM **g, g, return, DUMMYARG)
-DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
-
-#if QT_CONFIG(dtls)
-DEFINEFUNC2(int, DTLSv1_listen, SSL *s, s, BIO_ADDR *c, c, return -1, return)
-DEFINEFUNC(BIO_ADDR *, BIO_ADDR_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, BIO_ADDR_free, BIO_ADDR *ap, ap, return, DUMMYARG)
-DEFINEFUNC2(BIO_METHOD *, BIO_meth_new, int type, type, const char *name, name, return nullptr, return)
-DEFINEFUNC(void, BIO_meth_free, BIO_METHOD *biom, biom, return, DUMMYARG)
-DEFINEFUNC2(int, BIO_meth_set_write, BIO_METHOD *biom, biom, DgramWriteCallback write, write, return 0, return)
-DEFINEFUNC2(int, BIO_meth_set_read, BIO_METHOD *biom, biom, DgramReadCallback read, read, return 0, return)
-DEFINEFUNC2(int, BIO_meth_set_puts, BIO_METHOD *biom, biom, DgramPutsCallback puts, puts, return 0, return)
-DEFINEFUNC2(int, BIO_meth_set_ctrl, BIO_METHOD *biom, biom, DgramCtrlCallback ctrl, ctrl, return 0, return)
-DEFINEFUNC2(int, BIO_meth_set_create, BIO_METHOD *biom, biom, DgramCreateCallback crt, crt, return 0, return)
-DEFINEFUNC2(int, BIO_meth_set_destroy, BIO_METHOD *biom, biom, DgramDestroyCallback dtr, dtr, return 0, return)
-#endif // dtls
-
-#if QT_CONFIG(ocsp)
-DEFINEFUNC(const OCSP_CERTID *, OCSP_SINGLERESP_get0_id, const OCSP_SINGLERESP *x, x, return nullptr, return)
-DEFINEFUNC3(OCSP_RESPONSE *, d2i_OCSP_RESPONSE, OCSP_RESPONSE **a, a, const unsigned char **in, in, long len, len, return nullptr, return)
-DEFINEFUNC(void, OCSP_RESPONSE_free, OCSP_RESPONSE *rs, rs, return, DUMMYARG)
-DEFINEFUNC(OCSP_BASICRESP *, OCSP_response_get1_basic, OCSP_RESPONSE *resp, resp, return nullptr, return)
-DEFINEFUNC(void, OCSP_BASICRESP_free, OCSP_BASICRESP *bs, bs, return, DUMMYARG)
-DEFINEFUNC(int, OCSP_response_status, OCSP_RESPONSE *resp, resp, return OCSP_RESPONSE_STATUS_INTERNALERROR, return)
-DEFINEFUNC4(int, OCSP_basic_verify, OCSP_BASICRESP *bs, bs, STACK_OF(X509) *certs, certs, X509_STORE *st, st, unsigned long flags, flags, return -1, return)
-DEFINEFUNC(int, OCSP_resp_count, OCSP_BASICRESP *bs, bs, return 0, return)
-DEFINEFUNC2(OCSP_SINGLERESP *, OCSP_resp_get0, OCSP_BASICRESP *bs, bs, int idx, idx, return nullptr, return)
-DEFINEFUNC5(int, OCSP_single_get0_status, OCSP_SINGLERESP *single, single, int *reason, reason, ASN1_GENERALIZEDTIME **revtime, revtime,
- ASN1_GENERALIZEDTIME **thisupd, thisupd, ASN1_GENERALIZEDTIME **nextupd, nextupd, return -1, return)
-DEFINEFUNC4(int, OCSP_check_validity, ASN1_GENERALIZEDTIME *thisupd, thisupd, ASN1_GENERALIZEDTIME *nextupd, nextupd, long nsec, nsec, long maxsec, maxsec, return 0, return)
-DEFINEFUNC3(OCSP_CERTID *, OCSP_cert_to_id, const EVP_MD *dgst, dgst, X509 *subject, subject, X509 *issuer, issuer, return nullptr, return)
-DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG)
-DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd,
- ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid,
- return 0, return)
-DEFINEFUNC2(OCSP_RESPONSE *, OCSP_response_create, int status, status, OCSP_BASICRESP *bs, bs, return nullptr, return)
-DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return)
-DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return)
-DEFINEFUNC7(OCSP_SINGLERESP *, OCSP_basic_add1_status, OCSP_BASICRESP *r, r, OCSP_CERTID *c, c, int s, s,
- int re, re, ASN1_TIME *rt, rt, ASN1_TIME *t, t, ASN1_TIME *n, n, return nullptr, return)
-DEFINEFUNC(OCSP_BASICRESP *, OCSP_BASICRESP_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(int, i2d_OCSP_RESPONSE, OCSP_RESPONSE *r, r, unsigned char **ppout, ppout, return 0, return)
-DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer, EVP_PKEY *key, key,
- const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
-#endif // ocsp
-
-DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
-DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
-DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
-DEFINEFUNC(int, BIO_get_shutdown, BIO *a, a, return -1, return)
-DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG)
-
-DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return)
-DEFINEFUNC2(int, ASN1_INTEGER_cmp, const ASN1_INTEGER *a, a, const ASN1_INTEGER *b, b, return 1, return)
-DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
-DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return)
-DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
-DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
-DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return nullptr, return)
-DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
-
-DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
-DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
-DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
-#ifndef OPENSSL_NO_EC
-DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return nullptr, return)
-DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
-#endif
-DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
-DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return)
-DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return)
-DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
-DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
-DEFINEFUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, void, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, EVP_CIPHER_CTX_free, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
-DEFINEFUNC4(int, EVP_CIPHER_CTX_ctrl, EVP_CIPHER_CTX *ctx, ctx, int type, type, int arg, arg, void *ptr, ptr, return 0, return)
-DEFINEFUNC2(int, EVP_CIPHER_CTX_set_key_length, EVP_CIPHER_CTX *ctx, ctx, int keylen, keylen, return 0, return)
-DEFINEFUNC5(int, EVP_CipherInit, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *type, type, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
-DEFINEFUNC6(int, EVP_CipherInit_ex, EVP_CIPHER_CTX *ctx, ctx, const EVP_CIPHER *cipher, cipher, ENGINE *impl, impl, const unsigned char *key, key, const unsigned char *iv, iv, int enc, enc, return 0, return)
-DEFINEFUNC5(int, EVP_CipherUpdate, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, const unsigned char *in, in, int inl, inl, return 0, return)
-DEFINEFUNC3(int, EVP_CipherFinal, EVP_CIPHER_CTX *ctx, ctx, unsigned char *out, out, int *outl, outl, return 0, return)
-DEFINEFUNC(const EVP_MD *, EVP_get_digestbyname, const char *name, name, return nullptr, return)
-#ifndef OPENSSL_NO_DES
-DEFINEFUNC(const EVP_CIPHER *, EVP_des_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_RC2
-DEFINEFUNC(const EVP_CIPHER *, EVP_rc2_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-#ifndef OPENSSL_NO_AES
-DEFINEFUNC(const EVP_CIPHER *, EVP_aes_128_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const EVP_CIPHER *, EVP_aes_192_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const EVP_CIPHER *, EVP_aes_256_cbc, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif
-DEFINEFUNC(const EVP_MD *, EVP_sha1, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
-DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
-DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
-DEFINEFUNC2(int, EVP_PKEY_set1_DH, EVP_PKEY *a, a, DH *b, b, return -1, return)
-#ifndef OPENSSL_NO_EC
-DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
-#endif
-DEFINEFUNC2(int, EVP_PKEY_cmp, const EVP_PKEY *a, a, const EVP_PKEY *b, b, return -1, return)
-DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
-DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return nullptr, return)
-DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return nullptr, return)
-DEFINEFUNC(DH *, EVP_PKEY_get1_DH, EVP_PKEY *a, a, return nullptr, return)
-#ifndef OPENSSL_NO_EC
-DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
-#endif
-DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return)
-DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return)
-DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return nullptr, return)
-DEFINEFUNC(const char *, OBJ_nid2ln, int a, a, return nullptr, return)
-DEFINEFUNC(int, OBJ_sn2nid, const char *s, s, return 0, return)
-DEFINEFUNC(int, OBJ_ln2nid, const char *s, s, return 0, return)
-DEFINEFUNC3(int, i2t_ASN1_OBJECT, char *a, a, int b, b, ASN1_OBJECT *c, c, return -1, return)
-DEFINEFUNC4(int, OBJ_obj2txt, char *a, a, int b, b, ASN1_OBJECT *c, c, int d, d, return -1, return)
-DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return)
-DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-
-#ifndef OPENSSL_NO_EC
-DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
-DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return)
-#endif // OPENSSL_NO_EC
-
-DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
-DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
-DEFINEFUNC7(int, PEM_write_bio_PrivateKey, BIO *a, a, EVP_PKEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
-DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return nullptr, return)
-DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
-DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
-DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, return)
-DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
-DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
-DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return)
-DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
-DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
-DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
-DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return nullptr, return)
-DEFINEFUNC2(int, SSL_CIPHER_get_bits, const SSL_CIPHER *a, a, int *b, b, return 0, return)
-DEFINEFUNC(BIO *, SSL_get_rbio, const SSL *s, s, return nullptr, return)
-DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
-DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
-DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
-DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
-DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, return)
-DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
-DEFINEFUNC3(long, SSL_CTX_callback_ctrl, SSL_CTX *ctx, ctx, int dst, dst, GenericCallbackType cb, cb, return 0, return)
-DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
-DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG)
-DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG)
-DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return)
-DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
-DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
-DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
-DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
-DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return)
-DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return);
-DEFINEFUNC(void, SSL_CONF_CTX_free, SSL_CONF_CTX *a, a, return ,return);
-DEFINEFUNC2(void, SSL_CONF_CTX_set_ssl_ctx, SSL_CONF_CTX *a, a, SSL_CTX *b, b, return, return);
-DEFINEFUNC2(unsigned int, SSL_CONF_CTX_set_flags, SSL_CONF_CTX *a, a, unsigned int b, b, return 0, return);
-DEFINEFUNC(int, SSL_CONF_CTX_finish, SSL_CONF_CTX *a, a, return 0, return);
-DEFINEFUNC3(int, SSL_CONF_cmd, SSL_CONF_CTX *a, a, const char *b, b, const char *c, c, return 0, return);
-DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
-DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return nullptr, return)
-DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return nullptr, return)
-DEFINEFUNC(int, SSL_version, const SSL *a, a, return 0, return)
-DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
-DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return nullptr, return)
-DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return nullptr, return)
-DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
-DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return nullptr, return)
-DEFINEFUNC(SSL_CTX *, SSL_get_SSL_CTX, SSL *a, a, return nullptr, return)
-DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
-DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
-DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
-DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
-DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG)
-DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return)
-DEFINEFUNC(int, SSL_get_shutdown, const SSL *ssl, ssl, return 0, return)
-DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, return -1, return)
-DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
-DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return nullptr, return)
-DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return nullptr, return)
-DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return)
-DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return nullptr, return)
-
-#ifndef OPENSSL_NO_PSK
-DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
-DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG)
-DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return)
-#endif // !OPENSSL_NO_PSK
-
-DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return)
-DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return)
-DEFINEFUNC4(int, X509_digest, const X509 *x509, x509, const EVP_MD *type, type, unsigned char *md, md, unsigned int *len, len, return -1, return)
-DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
-DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
-DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
-DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
-//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
-DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
-DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG)
-DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
-DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
-DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
-DEFINEFUNC(const X509V3_EXT_METHOD *, X509V3_EXT_get, X509_EXTENSION *a, a, return nullptr, return)
-DEFINEFUNC(void *, X509V3_EXT_d2i, X509_EXTENSION *a, a, return nullptr, return)
-DEFINEFUNC(int, X509_EXTENSION_get_critical, X509_EXTENSION *a, a, return 0, return)
-DEFINEFUNC(ASN1_OCTET_STRING *, X509_EXTENSION_get_data, X509_EXTENSION *a, a, return nullptr, return)
-DEFINEFUNC(void, BASIC_CONSTRAINTS_free, BASIC_CONSTRAINTS *a, a, return, DUMMYARG)
-DEFINEFUNC(void, AUTHORITY_KEYID_free, AUTHORITY_KEYID *a, a, return, DUMMYARG)
-DEFINEFUNC(void, GENERAL_NAME_free, GENERAL_NAME *a, a, return, DUMMYARG)
-DEFINEFUNC2(int, ASN1_STRING_print, BIO *a, a, const ASN1_STRING *b, b, return 0, return)
-DEFINEFUNC2(int, X509_check_issued, X509 *a, a, X509 *b, b, return -1, return)
-DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return nullptr, return)
-DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return nullptr, return)
-DEFINEFUNC(ASN1_INTEGER *, X509_get_serialNumber, X509 *a, a, return nullptr, return)
-DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return)
-DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return)
-DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return nullptr, return)
-DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return nullptr, return)
-DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return nullptr, return)
-DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return nullptr, return)
-DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG)
-DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return)
-DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG)
-DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return)
-DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return)
-DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return)
-DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return)
-DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return)
-DEFINEFUNC(X509_STORE *, X509_STORE_CTX_get0_store, X509_STORE_CTX *ctx, ctx, return nullptr, return)
-DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return)
-DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return)
-DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
-DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
-DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
- const unsigned char *in, in, unsigned int inlen, inlen,
- const unsigned char *client, client, unsigned int client_len, client_len,
- return -1, return)
-DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
- int (*cb) (SSL *ssl, unsigned char **out,
- unsigned char *outlen,
- const unsigned char *in,
- unsigned int inlen, void *arg), cb,
- void *arg, arg, return, DUMMYARG)
-DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
- const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
-DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos,
- unsigned protos_len, protos_len, return -1, return)
-DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s,
- int (*cb) (SSL *ssl, const unsigned char **out,
- unsigned char *outlen,
- const unsigned char *in,
- unsigned int inlen, void *arg), cb,
- void *arg, arg, return, DUMMYARG)
-DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char **data, data,
- unsigned *len, len, return, DUMMYARG)
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
-// DTLS:
-#if QT_CONFIG(dtls)
-DEFINEFUNC2(void, SSL_CTX_set_cookie_generate_cb, SSL_CTX *ctx, ctx, CookieGenerateCallback cb, cb, return, DUMMYARG)
-DEFINEFUNC2(void, SSL_CTX_set_cookie_verify_cb, SSL_CTX *ctx, ctx, CookieVerifyCallback cb, cb, return, DUMMYARG)
-DEFINEFUNC(const SSL_METHOD *, DTLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(const SSL_METHOD *, DTLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
-#endif // dtls
-DEFINEFUNC2(void, BIO_set_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
-DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG)
-DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
-DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return)
-
-DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return)
-DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
-DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
-DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return)
-DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return)
-DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return)
-
-#ifndef OPENSSL_NO_EC
-DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
-DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
-DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
-DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return)
-DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return)
-#endif // OPENSSL_NO_EC
-
-DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \
- X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return);
-DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return nullptr, return);
-DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG)
-
-#define RESOLVEFUNC(func) \
- if (!(_q_##func = _q_PTR_##func(libs.ssl->resolve(#func))) \
- && !(_q_##func = _q_PTR_##func(libs.crypto->resolve(#func)))) \
- qsslSocketCannotResolveSymbolWarning(#func);
-
-#if !defined QT_LINKED_OPENSSL
-
-#if !QT_CONFIG(library)
-bool q_resolveOpenSslSymbols()
-{
- qCWarning(lcSsl, "QSslSocket: unable to resolve symbols. Qt is configured without the "
- "'library' feature, which means runtime resolving of libraries won't work.");
- qCWarning(lcSsl, "Either compile Qt statically or with support for runtime resolving "
- "of libraries.");
- return false;
-}
-#else
-
-# ifdef Q_OS_UNIX
-struct NumericallyLess
-{
- typedef bool result_type;
- result_type operator()(const QStringRef &lhs, const QStringRef &rhs) const
- {
- bool ok = false;
- int b = 0;
- int a = lhs.toInt(&ok);
- if (ok)
- b = rhs.toInt(&ok);
- if (ok) {
- // both toInt succeeded
- return a < b;
- } else {
- // compare as strings;
- return lhs < rhs;
- }
- }
-};
-
-struct LibGreaterThan
-{
- typedef bool result_type;
- result_type operator()(const QString &lhs, const QString &rhs) const
- {
- const QVector<QStringRef> lhsparts = lhs.splitRef(QLatin1Char('.'));
- const QVector<QStringRef> rhsparts = rhs.splitRef(QLatin1Char('.'));
- Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1);
-
- // note: checking rhs < lhs, the same as lhs > rhs
- return std::lexicographical_compare(rhsparts.begin() + 1, rhsparts.end(),
- lhsparts.begin() + 1, lhsparts.end(),
- NumericallyLess());
- }
-};
-
-#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
-static int dlIterateCallback(struct dl_phdr_info *info, size_t size, void *data)
-{
- if (size < sizeof (info->dlpi_addr) + sizeof (info->dlpi_name))
- return 1;
- QSet<QString> *paths = (QSet<QString> *)data;
- QString path = QString::fromLocal8Bit(info->dlpi_name);
- if (!path.isEmpty()) {
- QFileInfo fi(path);
- path = fi.absolutePath();
- if (!path.isEmpty())
- paths->insert(path);
- }
- return 0;
-}
-#endif
-
-static QStringList libraryPathList()
-{
- QStringList paths;
-# ifdef Q_OS_DARWIN
- paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH"))
- .split(QLatin1Char(':'), QString::SkipEmptyParts);
-
- // search in .app/Contents/Frameworks
- UInt32 packageType;
- CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, nullptr);
- if (packageType == FOUR_CHAR_CODE('APPL')) {
- QUrl bundleUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyBundleURL(CFBundleGetMainBundle())));
- QUrl frameworksUrl = QUrl::fromCFURL(QCFType<CFURLRef>(CFBundleCopyPrivateFrameworksURL(CFBundleGetMainBundle())));
- paths << bundleUrl.resolved(frameworksUrl).path();
- }
-# else
- paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH"))
- .split(QLatin1Char(':'), QString::SkipEmptyParts);
-# endif
- paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");
- paths << QLatin1String("/lib64") << QLatin1String("/usr/lib64") << QLatin1String("/usr/local/lib64");
- paths << QLatin1String("/lib32") << QLatin1String("/usr/lib32") << QLatin1String("/usr/local/lib32");
-
-#if defined(Q_OS_ANDROID)
- paths << QLatin1String("/system/lib");
-#elif defined(Q_OS_LINUX)
- // discover paths of already loaded libraries
- QSet<QString> loadedPaths;
- dl_iterate_phdr(dlIterateCallback, &loadedPaths);
- paths.append(loadedPaths.values());
-#endif
-
- return paths;
-}
-
-Q_NEVER_INLINE
-static QStringList findAllLibs(QLatin1String filter)
-{
- const QStringList paths = libraryPathList();
- QStringList found;
- const QStringList filters((QString(filter)));
-
- for (const QString &path : paths) {
- QDir dir(path);
- QStringList entryList = dir.entryList(filters, QDir::Files);
-
- std::sort(entryList.begin(), entryList.end(), LibGreaterThan());
- for (const QString &entry : qAsConst(entryList))
- found << path + QLatin1Char('/') + entry;
- }
-
- return found;
-}
-
-static QStringList findAllLibSsl()
-{
- return findAllLibs(QLatin1String("libssl.*"));
-}
-
-static QStringList findAllLibCrypto()
-{
- return findAllLibs(QLatin1String("libcrypto.*"));
-}
-# endif
-
-#ifdef Q_OS_WIN
-
-struct LoadedOpenSsl {
- std::unique_ptr<QSystemLibrary> ssl, crypto;
-};
-
-static bool tryToLoadOpenSslWin32Library(QLatin1String ssleay32LibName, QLatin1String libeay32LibName, LoadedOpenSsl &result)
-{
- auto ssleay32 = qt_make_unique<QSystemLibrary>(ssleay32LibName);
- if (!ssleay32->load(false)) {
- return FALSE;
- }
-
- auto libeay32 = qt_make_unique<QSystemLibrary>(libeay32LibName);
- if (!libeay32->load(false)) {
- return FALSE;
- }
-
- result.ssl = std::move(ssleay32);
- result.crypto = std::move(libeay32);
- return TRUE;
-}
-
-static LoadedOpenSsl loadOpenSsl()
-{
- LoadedOpenSsl result;
-
- // With OpenSSL 1.1 the names have changed to libssl-1_1(-x64) and libcrypto-1_1(-x64), for builds using
- // MSVC and GCC, (-x64 suffix for 64-bit builds).
-
-#ifdef Q_PROCESSOR_X86_64
-#define QT_SSL_SUFFIX "-x64"
-#else // !Q_PROCESSOFR_X86_64
-#define QT_SSL_SUFFIX
-#endif // !Q_PROCESSOR_x86_64
-
- tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX),
- QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result);
-
-#undef QT_SSL_SUFFIX
- return result;
-}
-#else
-
-struct LoadedOpenSsl {
- std::unique_ptr<QLibrary> ssl, crypto;
-};
-
-static LoadedOpenSsl loadOpenSsl()
-{
- LoadedOpenSsl result = {qt_make_unique<QLibrary>(), qt_make_unique<QLibrary>()};
-
-# if defined(Q_OS_UNIX)
- QLibrary * const libssl = result.ssl.get();
- QLibrary * const libcrypto = result.crypto.get();
-
- // Try to find the libssl library on the system.
- //
- // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that
- // is, libssl.so on most Unix systems. However, the .so file isn't present in
- // user installations because it's considered a development file.
- //
- // The right thing to do is to load the library at the major version we know how
- // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h)
- //
- // However, OpenSSL is a well-known case of binary-compatibility breakage. To
- // avoid such problems, many system integrators and Linux distributions change
- // the soname of the binary, letting the full version number be the soname. So
- // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that
- // reason, we will search a few common paths (see findAllLibSsl() above) in hopes
- // we find one that works.
- //
- // If that fails, for OpenSSL 1.0 we also try some fallbacks -- look up
- // libssl.so with a hardcoded soname. The reason is QTBUG-68156: the binary
- // builds of Qt happen (at the time of this writing) on RHEL machines,
- // which change SHLIB_VERSION_NUMBER to a non-portable string. When running
- // those binaries on the target systems, this code won't pick up
- // libssl.so.MODIFIED_SHLIB_VERSION_NUMBER because it doesn't exist there.
- // Given that the only 1.0 supported release (at the time of this writing)
- // is 1.0.2, with soname "1.0.0", give that a try too. Note that we mandate
- // OpenSSL >= 1.0.0 with a configure-time check, and OpenSSL has kept binary
- // compatibility between 1.0.0 and 1.0.2.
- //
- // It is important, however, to try the canonical name and the unversioned name
- // without going through the loop. By not specifying a path, we let the system
- // dlopen(3) function determine it for us. This will include any DT_RUNPATH or
- // DT_RPATH tags on our library header as well as other system-specific search
- // paths. See the man page for dlopen(3) on your system for more information.
-
-#ifdef Q_OS_OPENBSD
- libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
-#endif
-#if defined(SHLIB_VERSION_NUMBER) && !defined(Q_OS_QNX) // on QNX, the libs are always libssl.so and libcrypto.so
- // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER>
- libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER));
- libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER));
- if (libcrypto->load() && libssl->load()) {
- // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found
- return result;
- } else {
- libssl->unload();
- libcrypto->unload();
- }
-#endif
-
-#ifndef Q_OS_DARWIN
- // second attempt: find the development files libssl.so and libcrypto.so
- //
- // disabled on macOS/iOS:
- // macOS's /usr/lib/libssl.dylib, /usr/lib/libcrypto.dylib will be picked up in the third
- // attempt, _after_ <bundle>/Contents/Frameworks has been searched.
- // iOS does not ship a system libssl.dylib, libcrypto.dylib in the first place.
-# if defined(Q_OS_ANDROID)
- // OpenSSL 1.1.x must be suffixed otherwise it will use the system libcrypto.so libssl.so which on API-21 are OpenSSL 1.0 not 1.1
- auto openSSLSuffix = [](const QByteArray &defaultSuffix = {}) {
- auto suffix = qgetenv("ANDROID_OPENSSL_SUFFIX");
- if (suffix.isEmpty())
- return defaultSuffix;
- return suffix;
- };
-
- static QString suffix = QString::fromLatin1(openSSLSuffix("_1_1"));
-
- libssl->setFileNameAndVersion(QLatin1String("ssl") + suffix, -1);
- libcrypto->setFileNameAndVersion(QLatin1String("crypto") + suffix, -1);
-# else
- libssl->setFileNameAndVersion(QLatin1String("ssl"), -1);
- libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1);
-# endif
- if (libcrypto->load() && libssl->load()) {
- // libssl.so.0 and libcrypto.so.0 found
- return result;
- } else {
- libssl->unload();
- libcrypto->unload();
- }
-#endif
-
- // third attempt: loop on the most common library paths and find libssl
- const QStringList sslList = findAllLibSsl();
- const QStringList cryptoList = findAllLibCrypto();
-
- for (const QString &crypto : cryptoList) {
- libcrypto->setFileNameAndVersion(crypto, -1);
- if (libcrypto->load()) {
- QFileInfo fi(crypto);
- QString version = fi.completeSuffix();
-
- for (const QString &ssl : sslList) {
- if (!ssl.endsWith(version))
- continue;
-
- libssl->setFileNameAndVersion(ssl, -1);
-
- if (libssl->load()) {
- // libssl.so.x and libcrypto.so.x found
- return result;
- } else {
- libssl->unload();
- }
- }
- }
- libcrypto->unload();
- }
-
- // failed to load anything
- result = {};
- return result;
-
-# else
- // not implemented for this platform yet
- return result;
-# endif
-}
-#endif
-
-static QBasicMutex symbolResolveMutex;
-static QBasicAtomicInt symbolsResolved = Q_BASIC_ATOMIC_INITIALIZER(false);
-static bool triedToResolveSymbols = false;
-
-bool q_resolveOpenSslSymbols()
-{
- if (symbolsResolved.loadAcquire())
- return true;
- QMutexLocker locker(&symbolResolveMutex);
- if (symbolsResolved.loadRelaxed())
- return true;
- if (triedToResolveSymbols)
- return false;
- triedToResolveSymbols = true;
-
- LoadedOpenSsl libs = loadOpenSsl();
- if (!libs.ssl || !libs.crypto)
- // failed to load them
- return false;
-
- RESOLVEFUNC(OPENSSL_init_ssl)
- RESOLVEFUNC(OPENSSL_init_crypto)
- RESOLVEFUNC(ASN1_STRING_get0_data)
- RESOLVEFUNC(EVP_CIPHER_CTX_reset)
- RESOLVEFUNC(EVP_PKEY_up_ref)
- RESOLVEFUNC(EVP_PKEY_base_id)
- RESOLVEFUNC(RSA_bits)
- RESOLVEFUNC(OPENSSL_sk_new_null)
- RESOLVEFUNC(OPENSSL_sk_push)
- RESOLVEFUNC(OPENSSL_sk_free)
- RESOLVEFUNC(OPENSSL_sk_num)
- RESOLVEFUNC(OPENSSL_sk_pop_free)
- RESOLVEFUNC(OPENSSL_sk_value)
- RESOLVEFUNC(DH_get0_pqg)
- RESOLVEFUNC(SSL_CTX_set_options)
-
-#ifdef TLS1_3_VERSION
- RESOLVEFUNC(SSL_CTX_set_ciphersuites)
- RESOLVEFUNC(SSL_set_psk_use_session_callback)
-#endif // TLS 1.3 or OpenSSL > 1.1.1
-
- RESOLVEFUNC(SSL_get_client_random)
- RESOLVEFUNC(SSL_SESSION_get_master_key)
- RESOLVEFUNC(SSL_session_reused)
- RESOLVEFUNC(SSL_get_session)
- RESOLVEFUNC(SSL_set_options)
- RESOLVEFUNC(CRYPTO_get_ex_new_index)
- RESOLVEFUNC(TLS_method)
- RESOLVEFUNC(TLS_client_method)
- RESOLVEFUNC(TLS_server_method)
- RESOLVEFUNC(X509_up_ref)
- RESOLVEFUNC(X509_STORE_CTX_get0_chain)
- RESOLVEFUNC(X509_getm_notBefore)
- RESOLVEFUNC(X509_getm_notAfter)
- RESOLVEFUNC(X509_get_version)
- RESOLVEFUNC(X509_get_pubkey)
- RESOLVEFUNC(X509_STORE_set_verify_cb)
- RESOLVEFUNC(X509_STORE_set_ex_data)
- RESOLVEFUNC(X509_STORE_get_ex_data)
- RESOLVEFUNC(CRYPTO_free)
- RESOLVEFUNC(OpenSSL_version_num)
- RESOLVEFUNC(OpenSSL_version)
-
- if (!_q_OpenSSL_version) {
- // Apparently, we were built with OpenSSL 1.1 enabled but are now using
- // a wrong library.
- qCWarning(lcSsl, "Incompatible version of OpenSSL");
- return false;
- }
-
- RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint)
- RESOLVEFUNC(DH_bits)
- RESOLVEFUNC(DSA_bits)
-
-#if QT_CONFIG(dtls)
- RESOLVEFUNC(DTLSv1_listen)
- RESOLVEFUNC(BIO_ADDR_new)
- RESOLVEFUNC(BIO_ADDR_free)
- RESOLVEFUNC(BIO_meth_new)
- RESOLVEFUNC(BIO_meth_free)
- RESOLVEFUNC(BIO_meth_set_write)
- RESOLVEFUNC(BIO_meth_set_read)
- RESOLVEFUNC(BIO_meth_set_puts)
- RESOLVEFUNC(BIO_meth_set_ctrl)
- RESOLVEFUNC(BIO_meth_set_create)
- RESOLVEFUNC(BIO_meth_set_destroy)
-#endif // dtls
-
-#if QT_CONFIG(ocsp)
- RESOLVEFUNC(OCSP_SINGLERESP_get0_id)
- RESOLVEFUNC(d2i_OCSP_RESPONSE)
- RESOLVEFUNC(OCSP_RESPONSE_free)
- RESOLVEFUNC(OCSP_response_status)
- RESOLVEFUNC(OCSP_response_get1_basic)
- RESOLVEFUNC(OCSP_BASICRESP_free)
- RESOLVEFUNC(OCSP_basic_verify)
- RESOLVEFUNC(OCSP_resp_count)
- RESOLVEFUNC(OCSP_resp_get0)
- RESOLVEFUNC(OCSP_single_get0_status)
- RESOLVEFUNC(OCSP_check_validity)
- RESOLVEFUNC(OCSP_cert_to_id)
- RESOLVEFUNC(OCSP_id_get0_info)
- RESOLVEFUNC(OCSP_resp_get0_certs)
- RESOLVEFUNC(OCSP_basic_sign)
- RESOLVEFUNC(OCSP_response_create)
- RESOLVEFUNC(i2d_OCSP_RESPONSE)
- RESOLVEFUNC(OCSP_basic_add1_status)
- RESOLVEFUNC(OCSP_BASICRESP_new)
- RESOLVEFUNC(OCSP_CERTID_free)
- RESOLVEFUNC(OCSP_cert_to_id)
- RESOLVEFUNC(OCSP_id_cmp)
-#endif // ocsp
-
- RESOLVEFUNC(BIO_set_data)
- RESOLVEFUNC(BIO_get_data)
- RESOLVEFUNC(BIO_set_init)
- RESOLVEFUNC(BIO_get_shutdown)
- RESOLVEFUNC(BIO_set_shutdown)
- RESOLVEFUNC(ASN1_INTEGER_get)
- RESOLVEFUNC(ASN1_INTEGER_cmp)
- RESOLVEFUNC(ASN1_STRING_length)
- RESOLVEFUNC(ASN1_STRING_to_UTF8)
- RESOLVEFUNC(BIO_ctrl)
- RESOLVEFUNC(BIO_free)
- RESOLVEFUNC(BIO_new)
- RESOLVEFUNC(BIO_new_mem_buf)
- RESOLVEFUNC(BIO_read)
- RESOLVEFUNC(BIO_s_mem)
- RESOLVEFUNC(BIO_write)
- RESOLVEFUNC(BIO_set_flags)
- RESOLVEFUNC(BIO_clear_flags)
- RESOLVEFUNC(BIO_set_ex_data)
- RESOLVEFUNC(BIO_get_ex_data)
-
-#ifndef OPENSSL_NO_EC
- RESOLVEFUNC(EC_KEY_get0_group)
- RESOLVEFUNC(EC_GROUP_get_degree)
-#endif
- RESOLVEFUNC(BN_num_bits)
- RESOLVEFUNC(BN_is_word)
- RESOLVEFUNC(BN_mod_word)
- RESOLVEFUNC(DSA_new)
- RESOLVEFUNC(DSA_free)
- RESOLVEFUNC(ERR_error_string)
- RESOLVEFUNC(ERR_error_string_n)
- RESOLVEFUNC(ERR_get_error)
- RESOLVEFUNC(EVP_CIPHER_CTX_new)
- RESOLVEFUNC(EVP_CIPHER_CTX_free)
- RESOLVEFUNC(EVP_CIPHER_CTX_ctrl)
- RESOLVEFUNC(EVP_CIPHER_CTX_set_key_length)
- RESOLVEFUNC(EVP_CipherInit)
- RESOLVEFUNC(EVP_CipherInit_ex)
- RESOLVEFUNC(EVP_CipherUpdate)
- RESOLVEFUNC(EVP_CipherFinal)
- RESOLVEFUNC(EVP_get_digestbyname)
-#ifndef OPENSSL_NO_DES
- RESOLVEFUNC(EVP_des_cbc)
- RESOLVEFUNC(EVP_des_ede3_cbc)
-#endif
-#ifndef OPENSSL_NO_RC2
- RESOLVEFUNC(EVP_rc2_cbc)
-#endif
-#ifndef OPENSSL_NO_AES
- RESOLVEFUNC(EVP_aes_128_cbc)
- RESOLVEFUNC(EVP_aes_192_cbc)
- RESOLVEFUNC(EVP_aes_256_cbc)
-#endif
- RESOLVEFUNC(EVP_sha1)
- RESOLVEFUNC(EVP_PKEY_assign)
- RESOLVEFUNC(EVP_PKEY_set1_RSA)
- RESOLVEFUNC(EVP_PKEY_set1_DSA)
- RESOLVEFUNC(EVP_PKEY_set1_DH)
-
-#ifndef OPENSSL_NO_EC
- RESOLVEFUNC(EVP_PKEY_set1_EC_KEY)
- RESOLVEFUNC(EVP_PKEY_get1_EC_KEY)
- RESOLVEFUNC(PEM_read_bio_ECPrivateKey)
- RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
- RESOLVEFUNC(PEM_read_bio_EC_PUBKEY)
- RESOLVEFUNC(PEM_write_bio_EC_PUBKEY)
-#endif // OPENSSL_NO_EC
-
- RESOLVEFUNC(EVP_PKEY_cmp)
- RESOLVEFUNC(EVP_PKEY_free)
- RESOLVEFUNC(EVP_PKEY_get1_DSA)
- RESOLVEFUNC(EVP_PKEY_get1_RSA)
- RESOLVEFUNC(EVP_PKEY_get1_DH)
- RESOLVEFUNC(EVP_PKEY_new)
- RESOLVEFUNC(EVP_PKEY_type)
- RESOLVEFUNC(OBJ_nid2sn)
- RESOLVEFUNC(OBJ_nid2ln)
- RESOLVEFUNC(OBJ_sn2nid)
- RESOLVEFUNC(OBJ_ln2nid)
- RESOLVEFUNC(i2t_ASN1_OBJECT)
- RESOLVEFUNC(OBJ_obj2txt)
- RESOLVEFUNC(OBJ_obj2nid)
- RESOLVEFUNC(PEM_read_bio_PrivateKey)
- RESOLVEFUNC(PEM_read_bio_DSAPrivateKey)
- RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
- RESOLVEFUNC(PEM_read_bio_DHparams)
- RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
- RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
- RESOLVEFUNC(PEM_write_bio_PrivateKey)
- RESOLVEFUNC(PEM_read_bio_PUBKEY)
- RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY)
- RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
- RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY)
- RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
- RESOLVEFUNC(PEM_write_bio_PUBKEY)
- RESOLVEFUNC(RAND_seed)
- RESOLVEFUNC(RAND_status)
- RESOLVEFUNC(RAND_bytes)
- RESOLVEFUNC(RSA_new)
- RESOLVEFUNC(RSA_free)
- RESOLVEFUNC(SSL_CIPHER_description)
- RESOLVEFUNC(SSL_CIPHER_get_bits)
- RESOLVEFUNC(SSL_get_rbio)
- RESOLVEFUNC(SSL_CTX_check_private_key)
- RESOLVEFUNC(SSL_CTX_ctrl)
- RESOLVEFUNC(SSL_CTX_free)
- RESOLVEFUNC(SSL_CTX_new)
- RESOLVEFUNC(SSL_CTX_set_cipher_list)
- RESOLVEFUNC(SSL_CTX_callback_ctrl)
- RESOLVEFUNC(SSL_CTX_set_default_verify_paths)
- RESOLVEFUNC(SSL_CTX_set_verify)
- RESOLVEFUNC(SSL_CTX_set_verify_depth)
- RESOLVEFUNC(SSL_CTX_use_certificate)
- RESOLVEFUNC(SSL_CTX_use_certificate_file)
- RESOLVEFUNC(SSL_CTX_use_PrivateKey)
- RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
- RESOLVEFUNC(SSL_CTX_use_PrivateKey_file)
- RESOLVEFUNC(SSL_CTX_get_cert_store);
- RESOLVEFUNC(SSL_CONF_CTX_new);
- RESOLVEFUNC(SSL_CONF_CTX_free);
- RESOLVEFUNC(SSL_CONF_CTX_set_ssl_ctx);
- RESOLVEFUNC(SSL_CONF_CTX_set_flags);
- RESOLVEFUNC(SSL_CONF_CTX_finish);
- RESOLVEFUNC(SSL_CONF_cmd);
- RESOLVEFUNC(SSL_accept)
- RESOLVEFUNC(SSL_clear)
- RESOLVEFUNC(SSL_connect)
- RESOLVEFUNC(SSL_free)
- RESOLVEFUNC(SSL_get_ciphers)
- RESOLVEFUNC(SSL_get_current_cipher)
- RESOLVEFUNC(SSL_version)
- RESOLVEFUNC(SSL_get_error)
- RESOLVEFUNC(SSL_get_peer_cert_chain)
- RESOLVEFUNC(SSL_get_peer_certificate)
- RESOLVEFUNC(SSL_get_verify_result)
- RESOLVEFUNC(SSL_new)
- RESOLVEFUNC(SSL_get_SSL_CTX)
- RESOLVEFUNC(SSL_ctrl)
- RESOLVEFUNC(SSL_read)
- RESOLVEFUNC(SSL_set_accept_state)
- RESOLVEFUNC(SSL_set_bio)
- RESOLVEFUNC(SSL_set_connect_state)
- RESOLVEFUNC(SSL_shutdown)
- RESOLVEFUNC(SSL_get_shutdown)
- RESOLVEFUNC(SSL_set_session)
- RESOLVEFUNC(SSL_SESSION_free)
- RESOLVEFUNC(SSL_get1_session)
- RESOLVEFUNC(SSL_get_session)
- RESOLVEFUNC(SSL_set_ex_data)
- RESOLVEFUNC(SSL_get_ex_data)
- RESOLVEFUNC(SSL_get_ex_data_X509_STORE_CTX_idx)
-
-#ifndef OPENSSL_NO_PSK
- RESOLVEFUNC(SSL_set_psk_client_callback)
- RESOLVEFUNC(SSL_set_psk_server_callback)
- RESOLVEFUNC(SSL_CTX_use_psk_identity_hint)
-#endif // !OPENSSL_NO_PSK
-
- RESOLVEFUNC(SSL_write)
- RESOLVEFUNC(X509_NAME_entry_count)
- RESOLVEFUNC(X509_NAME_get_entry)
- RESOLVEFUNC(X509_NAME_ENTRY_get_data)
- RESOLVEFUNC(X509_NAME_ENTRY_get_object)
- RESOLVEFUNC(X509_PUBKEY_get)
- RESOLVEFUNC(X509_STORE_free)
- RESOLVEFUNC(X509_STORE_new)
- RESOLVEFUNC(X509_STORE_add_cert)
- RESOLVEFUNC(X509_STORE_CTX_free)
- RESOLVEFUNC(X509_STORE_CTX_init)
- RESOLVEFUNC(X509_STORE_CTX_new)
- RESOLVEFUNC(X509_STORE_CTX_set_purpose)
- RESOLVEFUNC(X509_STORE_CTX_get_error)
- RESOLVEFUNC(X509_STORE_CTX_get_error_depth)
- RESOLVEFUNC(X509_STORE_CTX_get_current_cert)
- RESOLVEFUNC(X509_STORE_CTX_get0_store)
- RESOLVEFUNC(X509_cmp)
- RESOLVEFUNC(X509_STORE_CTX_get_ex_data)
- RESOLVEFUNC(X509_dup)
- RESOLVEFUNC(X509_print)
- RESOLVEFUNC(X509_digest)
- RESOLVEFUNC(X509_EXTENSION_get_object)
- RESOLVEFUNC(X509_free)
- RESOLVEFUNC(X509_gmtime_adj)
- RESOLVEFUNC(ASN1_TIME_free)
- RESOLVEFUNC(X509_get_ext)
- RESOLVEFUNC(X509_get_ext_count)
- RESOLVEFUNC(X509_get_ext_d2i)
- RESOLVEFUNC(X509V3_EXT_get)
- RESOLVEFUNC(X509V3_EXT_d2i)
- RESOLVEFUNC(X509_EXTENSION_get_critical)
- RESOLVEFUNC(X509_EXTENSION_get_data)
- RESOLVEFUNC(BASIC_CONSTRAINTS_free)
- RESOLVEFUNC(AUTHORITY_KEYID_free)
- RESOLVEFUNC(GENERAL_NAME_free)
- RESOLVEFUNC(ASN1_STRING_print)
- RESOLVEFUNC(X509_check_issued)
- RESOLVEFUNC(X509_get_issuer_name)
- RESOLVEFUNC(X509_get_subject_name)
- RESOLVEFUNC(X509_get_serialNumber)
- RESOLVEFUNC(X509_verify_cert)
- RESOLVEFUNC(d2i_X509)
- RESOLVEFUNC(i2d_X509)
- RESOLVEFUNC(SSL_CTX_load_verify_locations)
- RESOLVEFUNC(i2d_SSL_SESSION)
- RESOLVEFUNC(d2i_SSL_SESSION)
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
- RESOLVEFUNC(SSL_select_next_proto)
- RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
- RESOLVEFUNC(SSL_get0_next_proto_negotiated)
- RESOLVEFUNC(SSL_set_alpn_protos)
- RESOLVEFUNC(SSL_CTX_set_alpn_select_cb)
- RESOLVEFUNC(SSL_get0_alpn_selected)
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
-#if QT_CONFIG(dtls)
- RESOLVEFUNC(SSL_CTX_set_cookie_generate_cb)
- RESOLVEFUNC(SSL_CTX_set_cookie_verify_cb)
- RESOLVEFUNC(DTLS_server_method)
- RESOLVEFUNC(DTLS_client_method)
-#endif // dtls
-
- RESOLVEFUNC(CRYPTO_malloc)
- RESOLVEFUNC(DH_new)
- RESOLVEFUNC(DH_free)
- RESOLVEFUNC(d2i_DHparams)
- RESOLVEFUNC(i2d_DHparams)
- RESOLVEFUNC(DH_check)
- RESOLVEFUNC(BN_bin2bn)
-
-#ifndef OPENSSL_NO_EC
- RESOLVEFUNC(EC_KEY_dup)
- RESOLVEFUNC(EC_KEY_new_by_curve_name)
- RESOLVEFUNC(EC_KEY_free)
- RESOLVEFUNC(EC_get_builtin_curves)
-#endif // OPENSSL_NO_EC
-
- RESOLVEFUNC(PKCS12_parse)
- RESOLVEFUNC(d2i_PKCS12_bio)
- RESOLVEFUNC(PKCS12_free)
-
- symbolsResolved.storeRelease(true);
- return true;
-}
-#endif // QT_CONFIG(library)
-
-#else // !defined QT_LINKED_OPENSSL
-
-bool q_resolveOpenSslSymbols()
-{
-#ifdef QT_NO_OPENSSL
- return false;
-#endif
- return true;
-}
-#endif // !defined QT_LINKED_OPENSSL
-
-//==============================================================================
-// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/
-// Based on X509_cmp_time() for intitial buffer hacking.
-//==============================================================================
-QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
-{
- size_t lTimeLength = aTime->length;
- char *pString = (char *) aTime->data;
-
- if (aTime->type == V_ASN1_UTCTIME) {
-
- char lBuffer[24];
- char *pBuffer = lBuffer;
-
- if ((lTimeLength < 11) || (lTimeLength > 17))
- return QDateTime();
-
- memcpy(pBuffer, pString, 10);
- pBuffer += 10;
- pString += 10;
-
- if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) {
- *pBuffer++ = '0';
- *pBuffer++ = '0';
- } else {
- *pBuffer++ = *pString++;
- *pBuffer++ = *pString++;
- // Skip any fractional seconds...
- if (*pString == '.') {
- pString++;
- while ((*pString >= '0') && (*pString <= '9'))
- pString++;
- }
- }
-
- *pBuffer++ = 'Z';
- *pBuffer++ = '\0';
-
- time_t lSecondsFromUCT;
- if (*pString == 'Z') {
- lSecondsFromUCT = 0;
- } else {
- if ((*pString != '+') && (*pString != '-'))
- return QDateTime();
-
- lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
- lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0');
- lSecondsFromUCT *= 60;
- if (*pString == '-')
- lSecondsFromUCT = -lSecondsFromUCT;
- }
-
- tm lTime;
- lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
- lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
- lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
- lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
- lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
- lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
- if (lTime.tm_year < 50)
- lTime.tm_year += 100; // RFC 2459
-
- QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday);
- QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
-
- QDateTime result(resDate, resTime, Qt::UTC);
- result = result.addSecs(lSecondsFromUCT);
- return result;
-
- } else if (aTime->type == V_ASN1_GENERALIZEDTIME) {
-
- if (lTimeLength < 15)
- return QDateTime(); // hopefully never triggered
-
- // generalized time is always YYYYMMDDHHMMSSZ (RFC 2459, section 4.1.2.5.2)
- tm lTime;
- lTime.tm_sec = ((pString[12] - '0') * 10) + (pString[13] - '0');
- lTime.tm_min = ((pString[10] - '0') * 10) + (pString[11] - '0');
- lTime.tm_hour = ((pString[8] - '0') * 10) + (pString[9] - '0');
- lTime.tm_mday = ((pString[6] - '0') * 10) + (pString[7] - '0');
- lTime.tm_mon = (((pString[4] - '0') * 10) + (pString[5] - '0'));
- lTime.tm_year = ((pString[0] - '0') * 1000) + ((pString[1] - '0') * 100) +
- ((pString[2] - '0') * 10) + (pString[3] - '0');
-
- QDate resDate(lTime.tm_year, lTime.tm_mon, lTime.tm_mday);
- QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
-
- QDateTime result(resDate, resTime, Qt::UTC);
- return result;
-
- } else {
- qCWarning(lcSsl, "unsupported date format detected");
- return QDateTime();
- }
-
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
deleted file mode 100644
index baf1a43113..0000000000
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ /dev/null
@@ -1,724 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/****************************************************************************
-**
-** In addition, as a special exception, the copyright holders listed above give
-** permission to link the code of its release of Qt with the OpenSSL project's
-** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the
-** same license as the original version), and distribute the linked executables.
-**
-** You must comply with the GNU General Public License version 2 in all
-** respects for all of the code used other than the "OpenSSL" code. If you
-** modify this file, you may extend this exception to your version of the file,
-** but you are not obligated to do so. If you do not wish to do so, delete
-** this exception statement from your version of this file.
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H
-#define QSSLSOCKET_OPENSSL_SYMBOLS_P_H
-
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qsslsocket_openssl_p.h"
-#include <QtCore/qglobal.h>
-
-#if QT_CONFIG(ocsp)
-#include "qocsp_p.h"
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#define DUMMYARG
-
-#if !defined QT_LINKED_OPENSSL
-// **************** Shared declarations ******************
-// ret func(arg)
-
-# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a); \
- }
-
-// ret func(arg1, arg2)
-# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func);\
- err; \
- } \
- funcret _q_##func(a, b); \
- }
-
-// ret func(arg1, arg2, arg3)
-# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c); \
- }
-
-// ret func(arg1, arg2, arg3, arg4)
-# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3, arg4) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c, d); \
- }
-
-// ret func(arg1, arg2, arg3, arg4, arg5)
-# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3, arg4, arg5) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c, d, e); \
- }
-
-// ret func(arg1, arg2, arg3, arg4, arg6)
-# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c, d, e, f); \
- }
-
-// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
-# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c, d, e, f, g); \
- }
-
-// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
-# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
- typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \
- static _q_PTR_##func _q_##func = 0; \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \
- if (Q_UNLIKELY(!_q_##func)) { \
- qsslSocketUnresolvedSymbolWarning(#func); \
- err; \
- } \
- funcret _q_##func(a, b, c, d, e, f, g, h, i); \
- }
-// **************** Shared declarations ******************
-
-#else // !defined QT_LINKED_OPENSSL
-
-// **************** Static declarations ******************
-
-// ret func(arg)
-# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
- ret q_##func(arg) { funcret func(a); }
-
-// ret func(arg1, arg2)
-# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
- ret q_##func(arg1, arg2) { funcret func(a, b); }
-
-// ret func(arg1, arg2, arg3)
-# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
- ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); }
-
-// ret func(arg1, arg2, arg3, arg4)
-# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
- ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); }
-
-// ret func(arg1, arg2, arg3, arg4, arg5)
-# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
- ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); }
-
-// ret func(arg1, arg2, arg3, arg4, arg6)
-# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); }
-
-// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
-# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); }
-
-// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
-# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
- ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); }
-
-// **************** Static declarations ******************
-
-#endif // !defined QT_LINKED_OPENSSL
-
-// TODO: the following lines previously were a part of 1.1 - specific header.
-// To reduce the amount of the change, I'm directly copying and pasting the
-// content of the header here. Later, can be better sorted/split into groups,
-// depending on the functionality.
-//#include "qsslsocket_openssl11_symbols_p.h"
-
-const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x);
-
-Q_AUTOTEST_EXPORT BIO *q_BIO_new(const BIO_METHOD *a);
-Q_AUTOTEST_EXPORT const BIO_METHOD *q_BIO_s_mem();
-
-int q_DSA_bits(DSA *a);
-int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_up_ref(EVP_PKEY *a);
-int q_EVP_PKEY_base_id(EVP_PKEY *a);
-int q_RSA_bits(RSA *a);
-Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
-Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
-Q_AUTOTEST_EXPORT OPENSSL_STACK *q_OPENSSL_sk_new_null();
-Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
-Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a);
-Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
-int q_SSL_session_reused(SSL *a);
-unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op);
-int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
-size_t q_SSL_get_client_random(SSL *a, unsigned char *out, size_t outlen);
-size_t q_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen);
-int q_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
-const SSL_METHOD *q_TLS_method();
-const SSL_METHOD *q_TLS_client_method();
-const SSL_METHOD *q_TLS_server_method();
-ASN1_TIME *q_X509_getm_notBefore(X509 *a);
-ASN1_TIME *q_X509_getm_notAfter(X509 *a);
-
-Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
-long q_X509_get_version(X509 *a);
-EVP_PKEY *q_X509_get_pubkey(X509 *a);
-void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
-int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data);
-void *q_X509_STORE_get_ex_data(X509_STORE *r, int idx);
-STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
-void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
-int q_DH_bits(DH *dh);
-
-# define q_SSL_load_error_strings() q_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS \
- | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)
-
-#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_OPENSSL_sk_num)(st)
-#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_OPENSSL_sk_value)(st, i)
-
-#define q_OPENSSL_add_all_algorithms_conf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
- | OPENSSL_INIT_ADD_ALL_DIGESTS \
- | OPENSSL_INIT_LOAD_CONFIG, NULL)
-#define q_OPENSSL_add_all_algorithms_noconf() q_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
- | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)
-
-int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
-void q_CRYPTO_free(void *str, const char *file, int line);
-
-long q_OpenSSL_version_num();
-const char *q_OpenSSL_version(int type);
-
-unsigned long q_SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session);
-unsigned long q_SSL_set_options(SSL *s, unsigned long op);
-
-#ifdef TLS1_3_VERSION
-int q_SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
-#endif
-
-#if QT_CONFIG(dtls)
-// Functions and types required for DTLS support:
-extern "C"
-{
-
-typedef int (*CookieVerifyCallback)(SSL *, const unsigned char *, unsigned);
-typedef int (*DgramWriteCallback) (BIO *, const char *, int);
-typedef int (*DgramReadCallback) (BIO *, char *, int);
-typedef int (*DgramPutsCallback) (BIO *, const char *);
-typedef long (*DgramCtrlCallback) (BIO *, int, long, void *);
-typedef int (*DgramCreateCallback) (BIO *);
-typedef int (*DgramDestroyCallback) (BIO *);
-
-}
-
-int q_DTLSv1_listen(SSL *s, BIO_ADDR *client);
-BIO_ADDR *q_BIO_ADDR_new();
-void q_BIO_ADDR_free(BIO_ADDR *ap);
-
-// API we need for a custom dgram BIO:
-
-BIO_METHOD *q_BIO_meth_new(int type, const char *name);
-void q_BIO_meth_free(BIO_METHOD *biom);
-int q_BIO_meth_set_write(BIO_METHOD *biom, DgramWriteCallback);
-int q_BIO_meth_set_read(BIO_METHOD *biom, DgramReadCallback);
-int q_BIO_meth_set_puts(BIO_METHOD *biom, DgramPutsCallback);
-int q_BIO_meth_set_ctrl(BIO_METHOD *biom, DgramCtrlCallback);
-int q_BIO_meth_set_create(BIO_METHOD *biom, DgramCreateCallback);
-int q_BIO_meth_set_destroy(BIO_METHOD *biom, DgramDestroyCallback);
-
-#endif // dtls
-
-void q_BIO_set_data(BIO *a, void *ptr);
-void *q_BIO_get_data(BIO *a);
-void q_BIO_set_init(BIO *a, int init);
-int q_BIO_get_shutdown(BIO *a);
-void q_BIO_set_shutdown(BIO *a, int shut);
-
-#if QT_CONFIG(ocsp)
-const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x);
-#endif // ocsp
-
-#define q_SSL_CTX_set_min_proto_version(ctx, version) \
- q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nullptr)
-
-#define q_SSL_CTX_set_max_proto_version(ctx, version) \
- q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr)
-
-extern "C" {
-typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *,
- SSL_SESSION **);
-}
-void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t);
-// Here the content of the 1.1 header ends.
-
-bool q_resolveOpenSslSymbols();
-long q_ASN1_INTEGER_get(ASN1_INTEGER *a);
-int q_ASN1_INTEGER_cmp(const ASN1_INTEGER *x, const ASN1_INTEGER *y);
-int q_ASN1_STRING_length(ASN1_STRING *a);
-int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
-long q_BIO_ctrl(BIO *a, int b, long c, void *d);
-Q_AUTOTEST_EXPORT int q_BIO_free(BIO *a);
-BIO *q_BIO_new_mem_buf(void *a, int b);
-int q_BIO_read(BIO *a, void *b, int c);
-Q_AUTOTEST_EXPORT int q_BIO_write(BIO *a, const void *b, int c);
-int q_BN_num_bits(const BIGNUM *a);
-int q_BN_is_word(BIGNUM *a, BN_ULONG w);
-BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w);
-
-#ifndef OPENSSL_NO_EC
-const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
-int q_EC_GROUP_get_degree(const EC_GROUP* g);
-#endif // OPENSSL_NO_EC
-
-DSA *q_DSA_new();
-void q_DSA_free(DSA *a);
-X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
-char *q_ERR_error_string(unsigned long a, char *b);
-void q_ERR_error_string_n(unsigned long e, char *buf, size_t len);
-unsigned long q_ERR_get_error();
-EVP_CIPHER_CTX *q_EVP_CIPHER_CTX_new();
-void q_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
-int q_EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
-int q_EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);
-int q_EVP_CipherInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, const unsigned char *key, const unsigned char *iv, int enc);
-int q_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
-int q_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
-int q_EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
-const EVP_MD *q_EVP_get_digestbyname(const char *name);
-
-#ifndef OPENSSL_NO_DES
-const EVP_CIPHER *q_EVP_des_cbc();
-const EVP_CIPHER *q_EVP_des_ede3_cbc();
-#endif // OPENSSL_NO_DES
-
-#ifndef OPENSSL_NO_RC2
-const EVP_CIPHER *q_EVP_rc2_cbc();
-#endif // OPENSSL_NO_RC2
-
-#ifndef OPENSSL_NO_AES
-const EVP_CIPHER *q_EVP_aes_128_cbc();
-const EVP_CIPHER *q_EVP_aes_192_cbc();
-const EVP_CIPHER *q_EVP_aes_256_cbc();
-#endif // OPENSSL_NO_AES
-
-Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1();
-int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b);
-
-#ifndef OPENSSL_NO_EC
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
-#endif
-
-Q_AUTOTEST_EXPORT int q_EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b);
-Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a);
-RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
-DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
-DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a);
-#ifndef OPENSSL_NO_EC
-EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a);
-#endif
-int q_EVP_PKEY_type(int a);
-Q_AUTOTEST_EXPORT EVP_PKEY *q_EVP_PKEY_new();
-int q_i2d_X509(X509 *a, unsigned char **b);
-const char *q_OBJ_nid2sn(int a);
-const char *q_OBJ_nid2ln(int a);
-int q_OBJ_sn2nid(const char *s);
-int q_OBJ_ln2nid(const char *s);
-int q_i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *obj);
-int q_OBJ_obj2txt(char *buf, int buf_len, ASN1_OBJECT *obj, int no_name);
-int q_OBJ_obj2nid(const ASN1_OBJECT *a);
-#define q_EVP_get_digestbynid(a) q_EVP_get_digestbyname(q_OBJ_nid2sn(a))
-Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
-DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
-RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
-
-#ifndef OPENSSL_NO_EC
-EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
-int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d,
- int e, pem_password_cb *f, void *g);
-EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
-int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
-#endif // OPENSSL_NO_EC
-
-DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d);
-int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
- int e, pem_password_cb *f, void *g);
-int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
- int e, pem_password_cb *f, void *g);
-int q_PEM_write_bio_PrivateKey(BIO *a, EVP_PKEY *b, const EVP_CIPHER *c, unsigned char *d,
- int e, pem_password_cb *f, void *g);
-Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
-DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
-RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
-int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
-int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
-int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b);
-
-void q_RAND_seed(const void *a, int b);
-int q_RAND_status();
-int q_RAND_bytes(unsigned char *b, int n);
-RSA *q_RSA_new();
-void q_RSA_free(RSA *a);
-int q_SSL_accept(SSL *a);
-int q_SSL_clear(SSL *a);
-char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c);
-int q_SSL_CIPHER_get_bits(const SSL_CIPHER *a, int *b);
-BIO *q_SSL_get_rbio(const SSL *s);
-int q_SSL_connect(SSL *a);
-int q_SSL_CTX_check_private_key(const SSL_CTX *a);
-long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
-void q_SSL_CTX_free(SSL_CTX *a);
-SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
-int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b);
-int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a);
-void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *));
-void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b);
-extern "C" {
-typedef void (*GenericCallbackType)();
-}
-long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType);
-int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
-int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
-int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
-int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
-int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c);
-X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a);
-SSL_CONF_CTX *q_SSL_CONF_CTX_new();
-void q_SSL_CONF_CTX_free(SSL_CONF_CTX *a);
-void q_SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *a, SSL_CTX *b);
-unsigned int q_SSL_CONF_CTX_set_flags(SSL_CONF_CTX *a, unsigned int b);
-int q_SSL_CONF_CTX_finish(SSL_CONF_CTX *a);
-int q_SSL_CONF_cmd(SSL_CONF_CTX *a, const char *b, const char *c);
-void q_SSL_free(SSL *a);
-STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a);
-const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
-int q_SSL_version(const SSL *a);
-int q_SSL_get_error(SSL *a, int b);
-STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
-X509 *q_SSL_get_peer_certificate(SSL *a);
-long q_SSL_get_verify_result(const SSL *a);
-SSL *q_SSL_new(SSL_CTX *a);
-SSL_CTX *q_SSL_get_SSL_CTX(SSL *a);
-long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg);
-int q_SSL_read(SSL *a, void *b, int c);
-void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
-void q_SSL_set_accept_state(SSL *a);
-void q_SSL_set_connect_state(SSL *a);
-int q_SSL_shutdown(SSL *a);
-int q_SSL_get_shutdown(const SSL *ssl);
-int q_SSL_set_session(SSL *to, SSL_SESSION *session);
-void q_SSL_SESSION_free(SSL_SESSION *ses);
-SSL_SESSION *q_SSL_get1_session(SSL *ssl);
-SSL_SESSION *q_SSL_get_session(const SSL *ssl);
-int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg);
-void *q_SSL_get_ex_data(const SSL *ssl, int idx);
-#ifndef OPENSSL_NO_PSK
-typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
-void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
-typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len);
-void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback);
-int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint);
-#endif // !OPENSSL_NO_PSK
-int q_SSL_write(SSL *a, const void *b, int c);
-int q_X509_cmp(X509 *a, X509 *b);
-X509 *q_X509_dup(X509 *a);
-void q_X509_print(BIO *a, X509*b);
-int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len);
-ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
-Q_AUTOTEST_EXPORT void q_X509_free(X509 *a);
-Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
-Q_AUTOTEST_EXPORT void q_ASN1_TIME_free(ASN1_TIME *t);
-X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
-int q_X509_get_ext_count(X509 *a);
-void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
-const X509V3_EXT_METHOD *q_X509V3_EXT_get(X509_EXTENSION *a);
-void *q_X509V3_EXT_d2i(X509_EXTENSION *a);
-int q_X509_EXTENSION_get_critical(X509_EXTENSION *a);
-ASN1_OCTET_STRING *q_X509_EXTENSION_get_data(X509_EXTENSION *a);
-void q_BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *a);
-void q_AUTHORITY_KEYID_free(AUTHORITY_KEYID *a);
-int q_ASN1_STRING_print(BIO *a, const ASN1_STRING *b);
-int q_X509_check_issued(X509 *a, X509 *b);
-X509_NAME *q_X509_get_issuer_name(X509 *a);
-X509_NAME *q_X509_get_subject_name(X509 *a);
-ASN1_INTEGER *q_X509_get_serialNumber(X509 *a);
-int q_X509_verify_cert(X509_STORE_CTX *ctx);
-int q_X509_NAME_entry_count(X509_NAME *a);
-X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b);
-ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a);
-ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a);
-EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a);
-void q_X509_STORE_free(X509_STORE *store);
-X509_STORE *q_X509_STORE_new();
-int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
-void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx);
-int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
- X509 *x509, STACK_OF(X509) *chain);
-X509_STORE_CTX *q_X509_STORE_CTX_new();
-int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
-int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
-int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
-X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
-X509_STORE *q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx);
-
-// Diffie-Hellman support
-DH *q_DH_new();
-void q_DH_free(DH *dh);
-DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length);
-int q_i2d_DHparams(DH *a, unsigned char **p);
-int q_DH_check(DH *dh, int *codes);
-
-BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
-#define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh)
-
-#ifndef OPENSSL_NO_EC
-// EC Diffie-Hellman support
-EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
-EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
-void q_EC_KEY_free(EC_KEY *ecdh);
-#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh)
-
-// EC curves management
-size_t q_EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems);
-int q_EC_curve_nist2nid(const char *name);
-#endif // OPENSSL_NO_EC
-
-#define q_SSL_get_server_tmp_key(ssl, key) q_SSL_ctrl((ssl), SSL_CTRL_GET_SERVER_TMP_KEY, 0, (char *)key)
-
-// PKCS#12 support
-int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca);
-PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12);
-void q_PKCS12_free(PKCS12 *pkcs12);
-
-#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
-#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
-#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
-#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st))
-#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i))
-
-void q_GENERAL_NAME_free(GENERAL_NAME *a);
-
-#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st))
-#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i))
-#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st))
-#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
-#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
- q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
-#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
- (char *)(rsa))
-#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
- (char *)(dsa))
-#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf()
-int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath);
-int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
-SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length);
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
- const unsigned char *in, unsigned int inlen,
- const unsigned char *client, unsigned int client_len);
-void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
- int (*cb) (SSL *ssl, unsigned char **out,
- unsigned char *outlen,
- const unsigned char *in,
- unsigned int inlen, void *arg),
- void *arg);
-void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
- unsigned *len);
-int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos,
- unsigned protos_len);
-void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
- int (*cb) (SSL *ssl,
- const unsigned char **out,
- unsigned char *outlen,
- const unsigned char *in,
- unsigned int inlen,
- void *arg), void *arg);
-void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
- unsigned *len);
-#endif // !OPENSSL_NO_NEXTPROTONEG
-
-
-#if QT_CONFIG(dtls)
-
-extern "C"
-{
-typedef int (*CookieGenerateCallback)(SSL *, unsigned char *, unsigned *);
-}
-
-void q_SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, CookieGenerateCallback cb);
-void q_SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, CookieVerifyCallback cb);
-const SSL_METHOD *q_DTLS_server_method();
-const SSL_METHOD *q_DTLS_client_method();
-
-#endif // dtls
-
-void *q_X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx);
-int q_SSL_get_ex_data_X509_STORE_CTX_idx();
-
-#if QT_CONFIG(dtls)
-#define q_DTLS_set_link_mtu(ssl, mtu) q_SSL_ctrl((ssl), DTLS_CTRL_SET_LINK_MTU, (mtu), nullptr)
-#define q_DTLSv1_get_timeout(ssl, arg) q_SSL_ctrl(ssl, DTLS_CTRL_GET_TIMEOUT, 0, arg)
-#define q_DTLSv1_handle_timeout(ssl) q_SSL_ctrl(ssl, DTLS_CTRL_HANDLE_TIMEOUT, 0, nullptr)
-#endif // dtls
-
-void q_BIO_set_flags(BIO *b, int flags);
-void q_BIO_clear_flags(BIO *b, int flags);
-void *q_BIO_get_ex_data(BIO *b, int idx);
-int q_BIO_set_ex_data(BIO *b, int idx, void *data);
-
-#define q_BIO_set_retry_read(b) q_BIO_set_flags(b, (BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY))
-#define q_BIO_set_retry_write(b) q_BIO_set_flags(b, (BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY))
-#define q_BIO_clear_retry_flags(b) q_BIO_clear_flags(b, (BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY))
-#define q_BIO_set_app_data(s,arg) q_BIO_set_ex_data(s,0,arg)
-#define q_BIO_get_app_data(s) q_BIO_get_ex_data(s,0)
-
-// Helper function
-class QDateTime;
-QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
-
-#define q_SSL_set_tlsext_status_type(ssl, type) \
- q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr)
-
-#if QT_CONFIG(ocsp)
-
-OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len);
-Q_AUTOTEST_EXPORT int q_i2d_OCSP_RESPONSE(OCSP_RESPONSE *r, unsigned char **ppout);
-Q_AUTOTEST_EXPORT OCSP_RESPONSE *q_OCSP_response_create(int status, OCSP_BASICRESP *bs);
-Q_AUTOTEST_EXPORT void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs);
-int q_OCSP_response_status(OCSP_RESPONSE *resp);
-OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp);
-Q_AUTOTEST_EXPORT OCSP_SINGLERESP *q_OCSP_basic_add1_status(OCSP_BASICRESP *rsp, OCSP_CERTID *cid,
- int status, int reason, ASN1_TIME *revtime,
- ASN1_TIME *thisupd, ASN1_TIME *nextupd);
-Q_AUTOTEST_EXPORT int q_OCSP_basic_sign(OCSP_BASICRESP *brsp, X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
- STACK_OF(X509) *certs, unsigned long flags);
-Q_AUTOTEST_EXPORT OCSP_BASICRESP *q_OCSP_BASICRESP_new();
-Q_AUTOTEST_EXPORT void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs);
-int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags);
-int q_OCSP_resp_count(OCSP_BASICRESP *bs);
-OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx);
-int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime,
- ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd);
-int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec);
-int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash,
- ASN1_INTEGER **pserial, OCSP_CERTID *cid);
-
-const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs);
-Q_AUTOTEST_EXPORT OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer);
-Q_AUTOTEST_EXPORT void q_OCSP_CERTID_free(OCSP_CERTID *cid);
-int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
-
-#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) \
- q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg)
-
-#define q_SSL_CTX_set_tlsext_status_cb(ssl, cb) \
- q_SSL_CTX_callback_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, GenericCallbackType(cb))
-
-# define q_SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
- q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, arglen, arg)
-
-#endif // ocsp
-
-
-void *q_CRYPTO_malloc(size_t num, const char *file, int line);
-#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 1abd18bb32..9dafb36a08 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSSLSOCKET_P_H
@@ -55,42 +19,26 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#include <private/qtcpsocket_p.h>
-#include "qsslkey.h"
-#include "qsslconfiguration_p.h"
+
#include "qocspresponse.h"
-#ifndef QT_NO_OPENSSL
-#include <private/qsslcontext_openssl_p.h>
-#else
-class QSslContext;
-#endif
+#include "qsslconfiguration_p.h"
+#include "qsslkey.h"
+#include "qtlsbackend_p.h"
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qstringlist.h>
-#include <QtCore/qvector.h>
-#include <private/qringbuffer_p.h>
-
-#if defined(Q_OS_MAC)
-#include <Security/SecCertificate.h>
-#include <CoreFoundation/CFArray.h>
-#elif defined(Q_OS_WIN)
-#include <QtCore/qt_windows.h>
-#ifndef Q_OS_WINRT
-#include <wincrypt.h>
-#endif // !Q_OS_WINRT
-#ifndef HCRYPTPROV_LEGACY
-#define HCRYPTPROV_LEGACY HCRYPTPROV
-#endif // !HCRYPTPROV_LEGACY
-#endif // Q_OS_WIN
+
+#include <memory>
QT_BEGIN_NAMESPACE
-#if defined(Q_OS_MACX)
- typedef CFDataRef (*PtrSecCertificateCopyData)(SecCertificateRef);
- typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*);
- typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
-#endif
+class QSslContext;
+class QTlsBackend;
-class QSslSocketPrivate : public QTcpSocketPrivate
+class Q_NETWORK_EXPORT QSslSocketPrivate : public QTcpSocketPrivate
{
Q_DECLARE_PUBLIC(QSslSocket)
public:
@@ -104,14 +52,11 @@ public:
QSslSocket::SslMode mode;
bool autoStartHandshake;
bool connectionEncrypted;
- bool shutdown;
bool ignoreAllSslErrors;
QList<QSslError> ignoreErrorsList;
bool* readyReadEmittedPointer;
QSslConfigurationPrivate configuration;
- QList<QSslError> sslErrors;
- QSharedPointer<QSslContext> sslContextPointer;
// if set, this hostname is used for certificate validation instead of the hostname
// that was used for connecting to.
@@ -122,41 +67,37 @@ public:
static bool s_loadRootCertsOnDemand;
static bool supportsSsl();
- static long sslLibraryVersionNumber();
- static QString sslLibraryVersionString();
- static long sslLibraryBuildVersionNumber();
- static QString sslLibraryBuildVersionString();
static void ensureInitialized();
+
static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> defaultDtlsCiphers();
static QList<QSslCipher> supportedCiphers();
static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
- static void resetDefaultCiphers();
- static QVector<QSslEllipticCurve> supportedEllipticCurves();
- static void setDefaultSupportedEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+ static QList<QSslEllipticCurve> supportedEllipticCurves();
+ static void setDefaultSupportedEllipticCurves(const QList<QSslEllipticCurve> &curves);
static void resetDefaultEllipticCurves();
static QList<QSslCertificate> defaultCaCertificates();
static QList<QSslCertificate> systemCaCertificates();
static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
- static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
- QRegExp::PatternSyntax syntax);
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
- Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QSslCertificate &cert,
- const QString &peerName);
- Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
+ static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
+ static bool isMatchingHostname(const QString &cn, const QString &hostname);
// The socket itself, including private slots.
- QTcpSocket *plainSocket;
+ QTcpSocket *plainSocket = nullptr;
void createPlainSocket(QIODevice::OpenMode openMode);
static void pauseSocketNotifiers(QSslSocket*);
static void resumeSocketNotifiers(QSslSocket*);
// ### The 2 methods below should be made member methods once the QSslContext class is made public
- static void checkSettingSslContext(QSslSocket*, QSharedPointer<QSslContext>);
- static QSharedPointer<QSslContext> sslContext(QSslSocket *socket);
+ static void checkSettingSslContext(QSslSocket*, std::shared_ptr<QSslContext>);
+ static std::shared_ptr<QSslContext> sslContext(QSslSocket *socket);
bool isPaused() const;
+ void setPaused(bool p);
bool bind(const QHostAddress &address, quint16, QAbstractSocket::BindMode) override;
void _q_connectedSlot();
void _q_hostFoundSlot();
@@ -171,49 +112,57 @@ public:
void _q_flushWriteBuffer();
void _q_flushReadBuffer();
void _q_resumeImplementation();
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !QT_CONFIG(schannel)
- virtual void _q_caRootLoaded(QSslCertificate,QSslCertificate) = 0;
-#endif
static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext
- virtual qint64 peek(char *data, qint64 maxSize) override;
- virtual QByteArray peek(qint64 maxSize) override;
- qint64 skip(qint64 maxSize) override;
+ qint64 peek(char *data, qint64 maxSize) override;
+ QByteArray peek(qint64 maxSize) override;
bool flush() override;
- // Platform specific functions
- virtual void startClientEncryption() = 0;
- virtual void startServerEncryption() = 0;
- virtual void transmit() = 0;
- virtual void disconnectFromHost() = 0;
- virtual void disconnected() = 0;
- virtual QSslCipher sessionCipher() const = 0;
- virtual QSsl::SslProtocol sessionProtocol() const = 0;
- virtual void continueHandshake() = 0;
-
- Q_AUTOTEST_EXPORT static bool rootCertOnDemandLoadingSupported();
-
-private:
- static bool ensureLibraryLoaded();
- static void ensureCiphersAndCertsLoaded();
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
- static QList<QByteArray> fetchSslCertificateData();
-#endif
+ void startClientEncryption();
+ void startServerEncryption();
+ void transmit();
+ void disconnectFromHost();
+ void disconnected();
+ QSslCipher sessionCipher() const;
+ QSsl::SslProtocol sessionProtocol() const;
+ void continueHandshake();
+
+ static bool rootCertOnDemandLoadingSupported();
+ static void setRootCertOnDemandLoadingSupported(bool supported);
+
+ static QTlsBackend *tlsBackendInUse();
+
+ // Needed by TlsCryptograph:
+ QSslSocket::SslMode tlsMode() const;
+ bool isRootsOnDemandAllowed() const;
+ QString verificationName() const;
+ QString tlsHostName() const;
+ QTcpSocket *plainTcpSocket() const;
+ bool verifyErrorsHaveBeenIgnored();
+ bool isAutoStartingHandshake() const;
+ bool isPendingClose() const;
+ void setPendingClose(bool pc);
+ qint64 maxReadBufferSize() const;
+ void setMaxReadBufferSize(qint64 maxSize);
+ void setEncrypted(bool enc);
+ QRingBufferRef &tlsWriteBuffer();
+ QRingBufferRef &tlsBuffer();
+ bool &tlsEmittedBytesWritten();
+ bool *readyReadPointer();
- static bool s_libraryLoaded;
- static bool s_loadedCiphersAndCerts;
protected:
- bool verifyErrorsHaveBeenIgnored();
+
+ bool hasUndecryptedData() const;
bool paused;
bool flushTriggered;
- QVector<QOcspResponse> ocspResponses;
-};
-#if QT_CONFIG(securetransport) || QT_CONFIG(schannel)
-// Implemented in qsslsocket_qt.cpp
-QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
-#endif
+ static inline QMutex backendMutex;
+ static inline QString activeBackendName;
+ static inline QTlsBackend *tlsBackend = nullptr;
+
+ std::unique_ptr<QTlsPrivate::TlsCryptograph> backend;
+};
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp
deleted file mode 100644
index 9ff9a66c05..0000000000
--- a/src/network/ssl/qsslsocket_qt.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QtCore/qbytearray.h>
-#include <QtCore/qdatastream.h>
-#include <QtCore/qmessageauthenticationcode.h>
-
-#include "qsslsocket_p.h"
-#include "qasn1element_p.h"
-#include "qsslkey_p.h"
-
-QT_BEGIN_NAMESPACE
-
-/*
- PKCS12 helpers.
-*/
-
-static QAsn1Element wrap(quint8 type, const QAsn1Element &child)
-{
- QByteArray value;
- QDataStream stream(&value, QIODevice::WriteOnly);
- child.write(stream);
- return QAsn1Element(type, value);
-}
-
-static QAsn1Element _q_PKCS7_data(const QByteArray &data)
-{
- QVector<QAsn1Element> items;
- items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1");
- items << wrap(QAsn1Element::Context0Type,
- QAsn1Element(QAsn1Element::OctetStringType, data));
- return QAsn1Element::fromVector(items);
-}
-
-/*!
- PKCS #12 key derivation.
-
- Some test vectors:
- http://www.drh-consultancy.demon.co.uk/test.txt
- \internal
-*/
-static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r)
-{
- const int u = 20;
- const int v = 64;
-
- // password formatting
- QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0');
- char *p = passUnicode.data();
- for (int i = 0; i < passPhrase.size(); ++i) {
- quint16 ch = passPhrase[i].unicode();
- *(p++) = (ch & 0xff00) >> 8;
- *(p++) = (ch & 0xff);
- }
-
- // prepare I
- QByteArray D(64, id);
- QByteArray S, P;
- const int sSize = v * ((salt.size() + v - 1) / v);
- S.resize(sSize);
- for (int i = 0; i < sSize; ++i)
- S[i] = salt[i % salt.size()];
- const int pSize = v * ((passUnicode.size() + v - 1) / v);
- P.resize(pSize);
- for (int i = 0; i < pSize; ++i)
- P[i] = passUnicode[i % passUnicode.size()];
- QByteArray I = S + P;
-
- // apply hashing
- const int c = (n + u - 1) / u;
- QByteArray A;
- QByteArray B;
- B.resize(v);
- QCryptographicHash hash(QCryptographicHash::Sha1);
- for (int i = 0; i < c; ++i) {
- // hash r iterations
- QByteArray Ai = D + I;
- for (int j = 0; j < r; ++j) {
- hash.reset();
- hash.addData(Ai);
- Ai = hash.result();
- }
-
- for (int j = 0; j < v; ++j)
- B[j] = Ai[j % u];
-
- // modify I as Ij = (Ij + B + 1) modulo 2^v
- for (int p = 0; p < I.size(); p += v) {
- quint8 carry = 1;
- for (int j = v - 1; j >= 0; --j) {
- quint16 v = quint8(I[p + j]) + quint8(B[j]) + carry;
- I[p + j] = v & 0xff;
- carry = (v & 0xff00) >> 8;
- }
- }
- A += Ai;
- }
- return A.left(n);
-}
-
-static QByteArray _q_PKCS12_salt()
-{
- QByteArray salt;
- salt.resize(8);
- for (int i = 0; i < salt.size(); ++i)
- salt[i] = (qrand() & 0xff);
- return salt;
-}
-
-static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert)
-{
- QVector<QAsn1Element> items;
- items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3");
-
- // certificate
- QVector<QAsn1Element> certItems;
- certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1");
- certItems << wrap(QAsn1Element::Context0Type,
- QAsn1Element(QAsn1Element::OctetStringType, cert.toDer()));
- items << wrap(QAsn1Element::Context0Type,
- QAsn1Element::fromVector(certItems));
-
- // local key id
- const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1);
- QVector<QAsn1Element> idItems;
- idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
- idItems << wrap(QAsn1Element::SetType,
- QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
- items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems));
-
- // dump
- QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
- QByteArray ba;
- QDataStream stream(&ba, QIODevice::WriteOnly);
- root.write(stream);
- return ba;
-}
-
-static QAsn1Element _q_PKCS12_key(const QSslKey &key)
-{
- Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa);
-
- QVector<QAsn1Element> keyItems;
- keyItems << QAsn1Element::fromInteger(0);
- QVector<QAsn1Element> algoItems;
- if (key.algorithm() == QSsl::Rsa)
- algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID);
- else if (key.algorithm() == QSsl::Dsa)
- algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID);
- algoItems << QAsn1Element(QAsn1Element::NullType);
- keyItems << QAsn1Element::fromVector(algoItems);
- keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer());
- return QAsn1Element::fromVector(keyItems);
-}
-
-static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId)
-{
- const int iterations = 2048;
- QByteArray salt = _q_PKCS12_salt();
- QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations);
- QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations);
-
- // prepare and encrypt data
- QByteArray plain;
- QDataStream plainStream(&plain, QIODevice::WriteOnly);
- _q_PKCS12_key(key).write(plainStream);
- QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
- plain, cKey, cIv);
-
- QVector<QAsn1Element> items;
- items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2");
-
- // key
- QVector<QAsn1Element> keyItems;
- QVector<QAsn1Element> algoItems;
- algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3");
- QVector<QAsn1Element> paramItems;
- paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt);
- paramItems << QAsn1Element::fromInteger(iterations);
- algoItems << QAsn1Element::fromVector(paramItems);
- keyItems << QAsn1Element::fromVector(algoItems);
- keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted);
- items << wrap(QAsn1Element::Context0Type,
- QAsn1Element::fromVector(keyItems));
-
- // local key id
- QVector<QAsn1Element> idItems;
- idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
- idItems << wrap(QAsn1Element::SetType,
- QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
- items << wrap(QAsn1Element::SetType,
- QAsn1Element::fromVector(idItems));
-
- // dump
- QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
- QByteArray ba;
- QDataStream stream(&ba, QIODevice::WriteOnly);
- root.write(stream);
- return ba;
-}
-
-static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
-{
- QVector<QAsn1Element> items;
-
- // certs
- for (int i = 0; i < certs.size(); ++i)
- items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i]));
-
- // key
- if (!key.isNull()) {
- const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1);
- items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId));
- }
-
- // dump
- QAsn1Element root = QAsn1Element::fromVector(items);
- QByteArray ba;
- QDataStream stream(&ba, QIODevice::WriteOnly);
- root.write(stream);
- return ba;
-}
-
-static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase)
-{
- const int iterations = 2048;
-
- // salt generation
- QByteArray macSalt = _q_PKCS12_salt();
- QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations);
-
- // HMAC calculation
- QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key);
- hmac.addData(data);
-
- QVector<QAsn1Element> algoItems;
- algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26");
- algoItems << QAsn1Element(QAsn1Element::NullType);
-
- QVector<QAsn1Element> digestItems;
- digestItems << QAsn1Element::fromVector(algoItems);
- digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result());
-
- QVector<QAsn1Element> macItems;
- macItems << QAsn1Element::fromVector(digestItems);
- macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt);
- macItems << QAsn1Element::fromInteger(iterations);
- return QAsn1Element::fromVector(macItems);
-}
-
-QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
-{
- QVector<QAsn1Element> items;
-
- // version
- items << QAsn1Element::fromInteger(3);
-
- // auth safe
- const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase);
- items << _q_PKCS7_data(data);
-
- // HMAC
- items << _q_PKCS12_mac(data, passPhrase);
-
- // dump
- QAsn1Element root = QAsn1Element::fromVector(items);
- QByteArray ba;
- QDataStream stream(&ba, QIODevice::WriteOnly);
- root.write(stream);
- return ba;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_schannel.cpp b/src/network/ssl/qsslsocket_schannel.cpp
deleted file mode 100644
index 31b0db4818..0000000000
--- a/src/network/ssl/qsslsocket_schannel.cpp
+++ /dev/null
@@ -1,2054 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// #define QSSLSOCKET_DEBUG
-
-#include "qssl_p.h"
-#include "qsslsocket.h"
-#include "qsslsocket_schannel_p.h"
-#include "qsslcertificate.h"
-#include "qsslcertificateextension.h"
-#include "qsslcertificate_p.h"
-#include "qsslcipher_p.h"
-
-#include <QtCore/qscopeguard.h>
-#include <QtCore/qoperatingsystemversion.h>
-#include <QtCore/qregularexpression.h>
-#include <QtCore/qdatastream.h>
-#include <QtCore/qmutex.h>
-
-#define SECURITY_WIN32
-#include <security.h>
-#include <schnlsp.h>
-
-#if NTDDI_VERSION >= NTDDI_WINBLUE && !defined(Q_CC_MINGW)
-// ALPN = Application Layer Protocol Negotiation
-#define SUPPORTS_ALPN 1
-#endif
-
-// Not defined in MinGW
-#ifndef SECBUFFER_ALERT
-#define SECBUFFER_ALERT 17
-#endif
-#ifndef SECPKG_ATTR_APPLICATION_PROTOCOL
-#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
-#endif
-
-// Another missing MinGW define
-#ifndef SEC_E_APPLICATION_PROTOCOL_MISMATCH
-#define SEC_E_APPLICATION_PROTOCOL_MISMATCH _HRESULT_TYPEDEF_(0x80090367L)
-#endif
-
-// Also not defined in MinGW.......
-#ifndef SP_PROT_TLS1_SERVER
-#define SP_PROT_TLS1_SERVER 0x00000040
-#endif
-#ifndef SP_PROT_TLS1_CLIENT
-#define SP_PROT_TLS1_CLIENT 0x00000080
-#endif
-#ifndef SP_PROT_TLS1_0_SERVER
-#define SP_PROT_TLS1_0_SERVER SP_PROT_TLS1_SERVER
-#endif
-#ifndef SP_PROT_TLS1_0_CLIENT
-#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
-#endif
-#ifndef SP_PROT_TLS1_0
-#define SP_PROT_TLS1_0 (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER)
-#endif
-#ifndef SP_PROT_TLS1_1_SERVER
-#define SP_PROT_TLS1_1_SERVER 0x00000100
-#endif
-#ifndef SP_PROT_TLS1_1_CLIENT
-#define SP_PROT_TLS1_1_CLIENT 0x00000200
-#endif
-#ifndef SP_PROT_TLS1_1
-#define SP_PROT_TLS1_1 (SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER)
-#endif
-#ifndef SP_PROT_TLS1_2_SERVER
-#define SP_PROT_TLS1_2_SERVER 0x00000400
-#endif
-#ifndef SP_PROT_TLS1_2_CLIENT
-#define SP_PROT_TLS1_2_CLIENT 0x00000800
-#endif
-#ifndef SP_PROT_TLS1_2
-#define SP_PROT_TLS1_2 (SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER)
-#endif
-#ifndef SP_PROT_TLS1_3_SERVER
-#define SP_PROT_TLS1_3_SERVER 0x00001000
-#endif
-#ifndef SP_PROT_TLS1_3_CLIENT
-#define SP_PROT_TLS1_3_CLIENT 0x00002000
-#endif
-#ifndef SP_PROT_TLS1_3
-#define SP_PROT_TLS1_3 (SP_PROT_TLS1_3_CLIENT | SP_PROT_TLS1_3_SERVER)
-#endif
-
-/*
- @future!:
-
- - Transmitting intermediate certificates
- - Look for a way to avoid putting intermediate certificates in the certificate store
- - No documentation on how to send the chain
- - A stackoverflow question on this from 3 years ago implies schannel only sends intermediate
- certificates if it's "in the system or user certificate store".
- - https://stackoverflow.com/q/30156584/2493610
- - This can be done by users, but we shouldn't add any and all local intermediate
- certs to the stores automatically.
- - PSK support
- - Was added in Windows 10 (it seems), documentation at time of writing is sparse/non-existent.
- - Specifically about how to supply credentials when they're requested.
- - Or how to recognize that they're requested in the first place.
- - Skip certificate verification.
- - Check if "PSK-only" is still required to do PSK _at all_ (all-around bad solution).
- - Check if SEC_I_INCOMPLETE_CREDENTIALS is still returned for both "missing certificate" and
- "missing PSK" when calling InitializeSecurityContext in "performHandshake".
-
- Medium priority:
- - Setting cipher-suites (or ALG_ID)
- - People have survived without it in WinRT
-
- Low priority:
- - Possibly make RAII wrappers for SecBuffer (which I commonly create QScopeGuards for)
-
-*/
-
-QT_BEGIN_NAMESPACE
-
-namespace {
-SecBuffer createSecBuffer(void *ptr, unsigned long length, unsigned long bufferType)
-{
- return SecBuffer{ length, bufferType, ptr };
-}
-
-SecBuffer createSecBuffer(QByteArray &buffer, unsigned long bufferType)
-{
- return createSecBuffer(buffer.data(), static_cast<unsigned long>(buffer.length()), bufferType);
-}
-
-QString schannelErrorToString(qint32 status)
-{
- switch (status) {
- case SEC_E_INSUFFICIENT_MEMORY:
- return QSslSocket::tr("Insufficient memory");
- case SEC_E_INTERNAL_ERROR:
- return QSslSocket::tr("Internal error");
- case SEC_E_INVALID_HANDLE:
- return QSslSocket::tr("An internal handle was invalid");
- case SEC_E_INVALID_TOKEN:
- return QSslSocket::tr("An internal token was invalid");
- case SEC_E_LOGON_DENIED:
- // According to the link below we get this error when Schannel receives TLS1_ALERT_ACCESS_DENIED
- // https://docs.microsoft.com/en-us/windows/desktop/secauthn/schannel-error-codes-for-tls-and-ssl-alerts
- return QSslSocket::tr("Access denied");
- case SEC_E_NO_AUTHENTICATING_AUTHORITY:
- return QSslSocket::tr("No authority could be contacted for authorization");
- case SEC_E_NO_CREDENTIALS:
- return QSslSocket::tr("No credentials");
- case SEC_E_TARGET_UNKNOWN:
- return QSslSocket::tr("The target is unknown or unreachable");
- case SEC_E_UNSUPPORTED_FUNCTION:
- return QSslSocket::tr("An unsupported function was requested");
- case SEC_E_WRONG_PRINCIPAL:
- // SNI error
- return QSslSocket::tr("The hostname provided does not match the one received from the peer");
- case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
- return QSslSocket::tr("No common protocol exists between the client and the server");
- case SEC_E_ILLEGAL_MESSAGE:
- return QSslSocket::tr("Unexpected or badly-formatted message received");
- case SEC_E_ENCRYPT_FAILURE:
- return QSslSocket::tr("The data could not be encrypted");
- case SEC_E_ALGORITHM_MISMATCH:
- return QSslSocket::tr("No cipher suites in common");
- case SEC_E_UNKNOWN_CREDENTIALS:
- // This can mean "invalid argument" in some cases...
- return QSslSocket::tr("The credentials were not recognized / Invalid argument");
- case SEC_E_MESSAGE_ALTERED:
- // According to the Internet it also triggers for messages that are out of order.
- // https://microsoft.public.platformsdk.security.narkive.com/4JAvlMvD/help-please-schannel-security-contexts-and-decryptmessage
- return QSslSocket::tr("The message was tampered with, damaged or out of sequence.");
- case SEC_E_OUT_OF_SEQUENCE:
- return QSslSocket::tr("A message was received out of sequence.");
- case SEC_E_CONTEXT_EXPIRED:
- return QSslSocket::tr("The TLS/SSL connection has been closed");
- default:
- return QSslSocket::tr("Unknown error occurred: %1").arg(status);
- }
-}
-
-DWORD toSchannelProtocol(QSsl::SslProtocol protocol)
-{
- DWORD protocols = SP_PROT_NONE;
- switch (protocol) {
- case QSsl::UnknownProtocol:
- return DWORD(-1);
- case QSsl::DtlsV1_0:
- case QSsl::DtlsV1_2:
- case QSsl::DtlsV1_0OrLater:
- case QSsl::DtlsV1_2OrLater:
- return DWORD(-1); // Not supported at the moment (@future)
- case QSsl::AnyProtocol:
- protocols = SP_PROT_TLS1_0 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
- // @future Add TLS 1.3 when supported by Windows!
- break;
- case QSsl::SslV2:
- case QSsl::SslV3:
- return DWORD(-1); // Not supported
- case QSsl::TlsV1SslV3:
- protocols = SP_PROT_TLS1_0;
- break;
- case QSsl::TlsV1_0:
- protocols = SP_PROT_TLS1_0;
- break;
- case QSsl::TlsV1_1:
- protocols = SP_PROT_TLS1_1;
- break;
- case QSsl::TlsV1_2:
- protocols = SP_PROT_TLS1_2;
- break;
- case QSsl::TlsV1_3:
- if ((false)) // @future[0/1] Replace with version check once it's supported in Windows
- protocols = SP_PROT_TLS1_3;
- else
- protocols = DWORD(-1);
- break;
- case QSsl::SecureProtocols: // TLS v1.0 and later is currently considered secure
- case QSsl::TlsV1_0OrLater:
- // For the "OrLater" protocols we fall through from one to the next, adding all of them
- // in ascending order
- protocols = SP_PROT_TLS1_0;
- Q_FALLTHROUGH();
- case QSsl::TlsV1_1OrLater:
- protocols |= SP_PROT_TLS1_1;
- Q_FALLTHROUGH();
- case QSsl::TlsV1_2OrLater:
- protocols |= SP_PROT_TLS1_2;
- Q_FALLTHROUGH();
- case QSsl::TlsV1_3OrLater:
- if ((false)) // @future[1/1] Also replace this with a version check
- protocols |= SP_PROT_TLS1_3;
- else if (protocol == QSsl::TlsV1_3OrLater)
- protocols = DWORD(-1); // if TlsV1_3OrLater was specifically chosen we should fail
- break;
- }
- return protocols;
-}
-
-/*!
- \internal
- Used when converting the established session's \a protocol back to
- Qt's own SslProtocol type.
-
- Only one protocol should be passed in at a time.
-*/
-QSsl::SslProtocol toQtSslProtocol(DWORD protocol)
-{
-#define MAP_PROTOCOL(sp_protocol, q_protocol) \
- if (protocol & sp_protocol) { \
- Q_ASSERT(!(protocol & ~sp_protocol)); \
- return q_protocol; \
- }
-
- MAP_PROTOCOL(SP_PROT_TLS1_0, QSsl::TlsV1_0)
- MAP_PROTOCOL(SP_PROT_TLS1_1, QSsl::TlsV1_1)
- MAP_PROTOCOL(SP_PROT_TLS1_2, QSsl::TlsV1_2)
- MAP_PROTOCOL(SP_PROT_TLS1_3, QSsl::TlsV1_3)
-#undef MAP_PROTOCOL
- Q_UNREACHABLE();
- return QSsl::UnknownProtocol;
-}
-
-/*!
- \internal
- Used by verifyCertContext to check if a client cert is used by a server or vice versa.
-*/
-bool netscapeWrongCertType(const QList<QSslCertificateExtension> &extensions, bool isClient)
-{
- const auto netscapeIt = std::find_if(
- extensions.cbegin(), extensions.cend(),
- [](const QSslCertificateExtension &extension) {
- const auto netscapeCertType = QStringLiteral("2.16.840.1.113730.1.1");
- return extension.oid() == netscapeCertType;
- });
- if (netscapeIt != extensions.cend()) {
- const QByteArray netscapeCertTypeByte = netscapeIt->value().toByteArray();
- int netscapeCertType = 0;
- QDataStream dataStream(netscapeCertTypeByte);
- dataStream >> netscapeCertType;
- if (dataStream.status() != QDataStream::Status::Ok)
- return true;
- const int expectedPeerCertType = isClient ? NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE
- : NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE;
- if ((netscapeCertType & expectedPeerCertType) == 0)
- return true;
- }
- return false;
-}
-
-/*!
- \internal
- Used by verifyCertContext to check the basicConstraints certificate
- extension to see if the certificate is a certificate authority.
- Returns false if the certificate does not have the basicConstraints
- extension or if it is not a certificate authority.
-*/
-bool isCertificateAuthority(const QList<QSslCertificateExtension> &extensions)
-{
- auto it = std::find_if(extensions.cbegin(), extensions.cend(),
- [](const QSslCertificateExtension &extension) {
- return extension.name() == QLatin1String("basicConstraints");
- });
- if (it != extensions.cend()) {
- QVariantMap basicConstraints = it->value().toMap();
- return basicConstraints.value(QLatin1String("ca"), false).toBool();
- }
- return false;
-}
-
-/*!
- \internal
- Returns true if the attributes we requested from the context/handshake have
- been given.
-*/
-bool matchesContextRequirements(DWORD attributes, DWORD requirements,
- QSslSocket::PeerVerifyMode verifyMode,
- bool isClient)
-{
-#ifdef QSSLSOCKET_DEBUG
-#define DEBUG_WARN(message) qCWarning(lcSsl, message)
-#else
-#define DEBUG_WARN(message)
-#endif
-
-#define CHECK_ATTRIBUTE(attributeName) \
- do { \
- const DWORD req##attributeName = isClient ? ISC_REQ_##attributeName : ASC_REQ_##attributeName; \
- const DWORD ret##attributeName = isClient ? ISC_RET_##attributeName : ASC_RET_##attributeName; \
- if (!(requirements & req##attributeName) != !(attributes & ret##attributeName)) { \
- DEBUG_WARN("Missing attribute \"" #attributeName "\""); \
- return false; \
- } \
- } while (false)
-
- CHECK_ATTRIBUTE(CONFIDENTIALITY);
- CHECK_ATTRIBUTE(REPLAY_DETECT);
- CHECK_ATTRIBUTE(SEQUENCE_DETECT);
- CHECK_ATTRIBUTE(STREAM);
- if (verifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
- CHECK_ATTRIBUTE(MUTUAL_AUTH);
-
- // This one is manual because there is no server / ASC_ version
- if (isClient) {
- const auto reqManualCredValidation = ISC_REQ_MANUAL_CRED_VALIDATION;
- const auto retManualCredValidation = ISC_RET_MANUAL_CRED_VALIDATION;
- if (!(requirements & reqManualCredValidation) != !(attributes & retManualCredValidation)) {
- DEBUG_WARN("Missing attribute \"MANUAL_CRED_VALIDATION\"");
- return false;
- }
- }
-
- return true;
-#undef CHECK_ATTRIBUTE
-#undef DEBUG_WARN
-}
-
-template<typename Required, typename Actual>
-Required const_reinterpret_cast(Actual *p)
-{
- return Required(p);
-}
-
-#ifdef SUPPORTS_ALPN
-bool supportsAlpn()
-{
- return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
-}
-
-QByteArray createAlpnString(const QByteArrayList &nextAllowedProtocols)
-{
- QByteArray alpnString;
- if (!nextAllowedProtocols.isEmpty() && supportsAlpn()) {
- const QByteArray names = [&nextAllowedProtocols]() {
- QByteArray protocolString;
- for (QByteArray proto : nextAllowedProtocols) {
- if (proto.size() > 255) {
- qCWarning(lcSsl) << "TLS ALPN extension" << proto
- << "is too long and will be ignored.";
- continue;
- } else if (proto.isEmpty()) {
- continue;
- }
- protocolString += char(proto.length()) + proto;
- }
- return protocolString;
- }();
- if (names.isEmpty())
- return alpnString;
-
- const quint16 namesSize = names.size();
- const quint32 alpnId = SecApplicationProtocolNegotiationExt_ALPN;
- const quint32 totalSize = sizeof(alpnId) + sizeof(namesSize) + namesSize;
- alpnString = QByteArray::fromRawData(reinterpret_cast<const char *>(&totalSize), sizeof(totalSize))
- + QByteArray::fromRawData(reinterpret_cast<const char *>(&alpnId), sizeof(alpnId))
- + QByteArray::fromRawData(reinterpret_cast<const char *>(&namesSize), sizeof(namesSize))
- + names;
- }
- return alpnString;
-}
-#endif // SUPPORTS_ALPN
-
-qint64 readToBuffer(QByteArray &buffer, QTcpSocket *plainSocket)
-{
- Q_ASSERT(plainSocket);
- static const qint64 shrinkCutoff = 1024 * 12;
- static const qint64 defaultRead = 1024 * 16;
- qint64 bytesRead = 0;
-
- const auto toRead = std::min(defaultRead, plainSocket->bytesAvailable());
- if (toRead > 0) {
- const auto bufferSize = buffer.size();
- buffer.reserve(bufferSize + toRead); // avoid growth strategy kicking in
- buffer.resize(bufferSize + toRead);
- bytesRead = plainSocket->read(buffer.data() + bufferSize, toRead);
- buffer.resize(bufferSize + bytesRead);
- // In case of excessive memory usage we shrink:
- if (buffer.size() < shrinkCutoff && buffer.capacity() > defaultRead)
- buffer.shrink_to_fit();
- }
-
- return bytesRead;
-}
-
-void retainExtraData(QByteArray &buffer, const SecBuffer &secBuffer)
-{
- Q_ASSERT(secBuffer.BufferType == SECBUFFER_EXTRA);
- if (int(secBuffer.cbBuffer) >= buffer.size())
- return;
-
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "We got SECBUFFER_EXTRA, will retain %lu bytes", secBuffer.cbBuffer);
-#endif
- std::move(buffer.end() - secBuffer.cbBuffer, buffer.end(), buffer.begin());
- buffer.resize(secBuffer.cbBuffer);
-}
-
-qint64 checkIncompleteData(const SecBuffer &secBuffer)
-{
- if (secBuffer.BufferType == SECBUFFER_MISSING) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Need %lu more bytes.", secBuffer.cbBuffer);
-#endif
- return secBuffer.cbBuffer;
-}
- return 0;
-}
-
-} // anonymous namespace
-
-bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
-bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
-Q_GLOBAL_STATIC(QRecursiveMutex, qt_schannel_mutex)
-
-void QSslSocketPrivate::ensureInitialized()
-{
- const QMutexLocker locker(qt_schannel_mutex);
- if (s_loadedCiphersAndCerts)
- return;
- s_loadedCiphersAndCerts = true;
-
- setDefaultCaCertificates(systemCaCertificates());
- s_loadRootCertsOnDemand = true; // setDefaultCaCertificates sets it to false, re-enable it.
-
- resetDefaultCiphers();
-}
-
-void QSslSocketPrivate::resetDefaultCiphers()
-{
- setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
- setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
-}
-
-void QSslSocketPrivate::resetDefaultEllipticCurves()
-{
- Q_UNIMPLEMENTED();
-}
-
-bool QSslSocketPrivate::supportsSsl()
-{
- return true;
-}
-
-QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
-{
- // Copied from qsslsocket_openssl.cpp's systemCaCertificates function.
- QList<QSslCertificate> systemCerts;
- auto hSystemStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
- if (hSystemStore) {
- PCCERT_CONTEXT pc = nullptr;
- while ((pc = CertFindCertificateInStore(hSystemStore.get(), X509_ASN_ENCODING, 0,
- CERT_FIND_ANY, nullptr, pc))) {
- systemCerts.append(QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(pc));
- }
- }
- return systemCerts;
-}
-
-long QSslSocketPrivate::sslLibraryVersionNumber()
-{
- const auto os = QOperatingSystemVersion::current();
- return (os.majorVersion() << 24) | ((os.minorVersion() & 0xFF) << 16) | (os.microVersion() & 0xFFFF);
-}
-
-QString QSslSocketPrivate::sslLibraryVersionString()
-{
- const auto os = QOperatingSystemVersion::current();
- return QString::fromLatin1("Secure Channel, %1 %2.%3.%4")
- .arg(os.name(),
- QString::number(os.majorVersion()),
- QString::number(os.minorVersion()),
- QString::number(os.microVersion()));
-}
-
-long QSslSocketPrivate::sslLibraryBuildVersionNumber()
-{
- // There is no separate build version
- return sslLibraryVersionNumber();
-}
-
-QString QSslSocketPrivate::sslLibraryBuildVersionString()
-{
- const auto os = QOperatingSystemVersion::current();
- return QString::fromLatin1("%1.%2.%3")
- .arg(QString::number(os.majorVersion()),
- QString::number(os.minorVersion()),
- QString::number(os.microVersion()));
-}
-
-QSslSocketBackendPrivate::QSslSocketBackendPrivate()
-{
- SecInvalidateHandle(&credentialHandle);
- SecInvalidateHandle(&contextHandle);
- ensureInitialized();
-}
-
-QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
-{
- closeCertificateStores();
- deallocateContext();
- freeCredentialsHandle();
- CertFreeCertificateContext(localCertContext);
-}
-
-bool QSslSocketBackendPrivate::sendToken(void *token, unsigned long tokenLength, bool emitError)
-{
- if (tokenLength == 0)
- return true;
- const qint64 written = plainSocket->write(static_cast<const char *>(token), tokenLength);
- if (written != qint64(tokenLength)) {
- // Failed to write/buffer everything or an error occurred
- if (emitError)
- setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
- return false;
- }
- return true;
-}
-
-QString QSslSocketBackendPrivate::targetName() const
-{
- // Used for SNI extension
- return verificationPeerName.isEmpty() ? q_func()->peerName() : verificationPeerName;
-}
-
-ULONG QSslSocketBackendPrivate::getContextRequirements()
-{
- const bool isClient = mode == QSslSocket::SslClientMode;
- ULONG req = 0;
-
- req |= ISC_REQ_ALLOCATE_MEMORY; // Allocate memory for buffers automatically
- req |= ISC_REQ_CONFIDENTIALITY; // Encrypt messages
- req |= ISC_REQ_REPLAY_DETECT; // Detect replayed messages
- req |= ISC_REQ_SEQUENCE_DETECT; // Detect out of sequence messages
- req |= ISC_REQ_STREAM; // Support a stream-oriented connection
-
- if (isClient) {
- req |= ISC_REQ_MANUAL_CRED_VALIDATION; // Manually validate certificate
- } else {
- switch (configuration.peerVerifyMode) {
- case QSslSocket::PeerVerifyMode::VerifyNone:
- // There doesn't seem to be a way to ask for an optional client cert :-(
- case QSslSocket::PeerVerifyMode::AutoVerifyPeer:
- case QSslSocket::PeerVerifyMode::QueryPeer:
- break;
- case QSslSocket::PeerVerifyMode::VerifyPeer:
- req |= ISC_REQ_MUTUAL_AUTH;
- break;
- }
- }
-
- return req;
-}
-
-bool QSslSocketBackendPrivate::acquireCredentialsHandle()
-{
- Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
-
- const bool isClient = mode == QSslSocket::SslClientMode;
- const DWORD protocols = toSchannelProtocol(configuration.protocol);
- if (protocols == DWORD(-1)) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QSslSocket::tr("Invalid protocol chosen"));
- return false;
- }
-
- const CERT_CHAIN_CONTEXT *chainContext = nullptr;
- auto freeCertChain = qScopeGuard([&chainContext]() {
- if (chainContext)
- CertFreeCertificateChain(chainContext);
- });
-
- DWORD certsCount = 0;
- // Set up our certificate stores before trying to use one...
- initializeCertificateStores();
-
- // Check if user has specified a certificate chain but it could not be loaded.
- // This happens if there was something wrong with the certificate chain or there was no private
- // key.
- if (!configuration.localCertificateChain.isEmpty() && !localCertificateStore)
- return true; // 'true' because "tst_QSslSocket::setEmptyKey" expects us to not disconnect
-
- if (localCertificateStore != nullptr) {
- CERT_CHAIN_FIND_BY_ISSUER_PARA findParam;
- ZeroMemory(&findParam, sizeof(findParam));
- findParam.cbSize = sizeof(findParam);
- findParam.pszUsageIdentifier = isClient ? szOID_PKIX_KP_CLIENT_AUTH : szOID_PKIX_KP_SERVER_AUTH;
-
- // There should only be one chain in our store, so.. we grab that one.
- chainContext = CertFindChainInStore(localCertificateStore.get(),
- X509_ASN_ENCODING,
- 0,
- CERT_CHAIN_FIND_BY_ISSUER,
- &findParam,
- nullptr);
- if (!chainContext) {
- const QString message = isClient
- ? QSslSocket::tr("The certificate provided cannot be used for a client.")
- : QSslSocket::tr("The certificate provided cannot be used for a server.");
- setErrorAndEmit(QAbstractSocket::SocketError::SslInvalidUserDataError, message);
- return false;
- }
- Q_ASSERT(chainContext->cChain == 1);
- Q_ASSERT(chainContext->rgpChain[0]);
- Q_ASSERT(chainContext->rgpChain[0]->cbSize >= 1);
- Q_ASSERT(chainContext->rgpChain[0]->rgpElement[0]);
- Q_ASSERT(!localCertContext);
- localCertContext = CertDuplicateCertificateContext(chainContext->rgpChain[0]
- ->rgpElement[0]
- ->pCertContext);
- certsCount = 1;
- Q_ASSERT(localCertContext);
- }
-
- SCHANNEL_CRED cred{
- SCHANNEL_CRED_VERSION, // dwVersion
- certsCount, // cCreds
- &localCertContext, // paCred (certificate(s) containing a private key for authentication)
- nullptr, // hRootStore
-
- 0, // cMappers (reserved)
- nullptr, // aphMappers (reserved)
-
- 0, // cSupportedAlgs
- nullptr, // palgSupportedAlgs (nullptr = system default) @future: QSslCipher-related
-
- protocols, // grbitEnabledProtocols
- 0, // dwMinimumCipherStrength (0 = system default)
- 0, // dwMaximumCipherStrength (0 = system default)
- 0, // dwSessionLifespan (0 = schannel default, 10 hours)
- SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
- | SCH_CRED_NO_DEFAULT_CREDS, // dwFlags
- 0 // dwCredFormat (must be 0)
- };
-
- TimeStamp expiration{};
- auto status = AcquireCredentialsHandle(nullptr, // pszPrincipal (unused)
- const_cast<wchar_t *>(UNISP_NAME), // pszPackage
- isClient ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND, // fCredentialUse
- nullptr, // pvLogonID (unused)
- &cred, // pAuthData
- nullptr, // pGetKeyFn (unused)
- nullptr, // pvGetKeyArgument (unused)
- &credentialHandle, // phCredential
- &expiration // ptsExpir
- );
-
- if (status != SEC_E_OK) {
- setErrorAndEmit(QAbstractSocket::SslInternalError, schannelErrorToString(status));
- return false;
- }
- return true;
-}
-
-void QSslSocketBackendPrivate::deallocateContext()
-{
- if (SecIsValidHandle(&contextHandle)) {
- DeleteSecurityContext(&contextHandle);
- SecInvalidateHandle(&contextHandle);
- }
-}
-
-void QSslSocketBackendPrivate::freeCredentialsHandle()
-{
- if (SecIsValidHandle(&credentialHandle)) {
- FreeCredentialsHandle(&credentialHandle);
- SecInvalidateHandle(&credentialHandle);
- }
-}
-
-void QSslSocketBackendPrivate::closeCertificateStores()
-{
- localCertificateStore.reset();
- peerCertificateStore.reset();
- caCertificateStore.reset();
-}
-
-bool QSslSocketBackendPrivate::createContext()
-{
- Q_ASSERT(SecIsValidHandle(&credentialHandle));
- Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
- Q_ASSERT(mode == QSslSocket::SslClientMode);
- ULONG contextReq = getContextRequirements();
-
- SecBuffer outBuffers[3];
- outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
- outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
- outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- auto freeBuffers = qScopeGuard([&outBuffers]() {
- for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
- if (outBuffers[i].pvBuffer)
- FreeContextBuffer(outBuffers[i].pvBuffer);
- }
- });
- SecBufferDesc outputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(outBuffers),
- outBuffers
- };
-
- TimeStamp expiry;
-
- SecBufferDesc alpnBufferDesc;
- bool useAlpn = false;
-#ifdef SUPPORTS_ALPN
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
- QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
- useAlpn = !alpnString.isEmpty();
- SecBuffer alpnBuffers[1];
- alpnBuffers[0] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
- alpnBufferDesc = {
- SECBUFFER_VERSION,
- ARRAYSIZE(alpnBuffers),
- alpnBuffers
- };
-#endif
-
- auto status = InitializeSecurityContext(&credentialHandle, // phCredential
- nullptr, // phContext
- const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
- contextReq, // fContextReq
- 0, // Reserved1
- 0, // TargetDataRep (unused)
- useAlpn ? &alpnBufferDesc : nullptr, // pInput
- 0, // Reserved2
- &contextHandle, // phNewContext
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr
- &expiry // ptsExpiry
- );
-
- // This is the first call to InitializeSecurityContext, so theoretically "CONTINUE_NEEDED"
- // should be the only non-error return-code here.
- if (status != SEC_I_CONTINUE_NEEDED) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
- return false;
- }
-
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
- return false;
- schannelState = SchannelState::PerformHandshake;
- return true;
-}
-
-bool QSslSocketBackendPrivate::acceptContext()
-{
- Q_ASSERT(SecIsValidHandle(&credentialHandle));
- Q_ASSERT(schannelState == SchannelState::InitializeHandshake);
- Q_ASSERT(mode == QSslSocket::SslServerMode);
- ULONG contextReq = getContextRequirements();
-
- if (missingData > plainSocket->bytesAvailable())
- return true;
-
- missingData = 0;
- readToBuffer(intermediateBuffer, plainSocket);
- if (intermediateBuffer.isEmpty())
- return true; // definitely need more data..
-
- SecBuffer inBuffers[2];
- inBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
-
-#ifdef SUPPORTS_ALPN
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNone;
- // The string must be alive when we call AcceptSecurityContext
- QByteArray alpnString = createAlpnString(configuration.nextAllowedProtocols);
- if (!alpnString.isEmpty()) {
- inBuffers[1] = createSecBuffer(alpnString, SECBUFFER_APPLICATION_PROTOCOLS);
- } else
-#endif
- {
- inBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- }
-
- SecBufferDesc inputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(inBuffers),
- inBuffers
- };
-
- SecBuffer outBuffers[3];
- outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
- outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
- outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- auto freeBuffers = qScopeGuard([&outBuffers]() {
- for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
- if (outBuffers[i].pvBuffer)
- FreeContextBuffer(outBuffers[i].pvBuffer);
- }
- });
- SecBufferDesc outputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(outBuffers),
- outBuffers
- };
-
- TimeStamp expiry;
- auto status = AcceptSecurityContext(
- &credentialHandle, // phCredential
- nullptr, // phContext
- &inputBufferDesc, // pInput
- contextReq, // fContextReq
- 0, // TargetDataRep (unused)
- &contextHandle, // phNewContext
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr
- &expiry // ptsTimeStamp
- );
-
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- // Need more data
- missingData = checkIncompleteData(outBuffers[0]);
- return true;
- }
-
- if (inBuffers[1].BufferType == SECBUFFER_EXTRA) {
- // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
- // inBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
- // be stored.
- retainExtraData(intermediateBuffer, inBuffers[1]);
- } else { /* No 'extra' data, message not incomplete */
- intermediateBuffer.resize(0);
- }
-
- if (status != SEC_I_CONTINUE_NEEDED) {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Error creating SSL context (%1)").arg(schannelErrorToString(status)));
- return false;
- }
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
- return false;
- schannelState = SchannelState::PerformHandshake;
- return true;
-}
-
-bool QSslSocketBackendPrivate::performHandshake()
-{
- if (plainSocket->state() == QAbstractSocket::UnconnectedState) {
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- QSslSocket::tr("The TLS/SSL connection has been closed"));
- return false;
- }
- Q_ASSERT(SecIsValidHandle(&credentialHandle));
- Q_ASSERT(SecIsValidHandle(&contextHandle));
- Q_ASSERT(schannelState == SchannelState::PerformHandshake);
-
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Bytes available from socket: %lld", plainSocket->bytesAvailable());
- qCDebug(lcSsl, "intermediateBuffer size: %d", intermediateBuffer.size());
-#endif
-
- if (missingData > plainSocket->bytesAvailable())
- return true;
-
- missingData = 0;
- readToBuffer(intermediateBuffer, plainSocket);
- if (intermediateBuffer.isEmpty())
- return true; // no data, will fail
-
- SecBuffer inputBuffers[2];
- inputBuffers[0] = createSecBuffer(intermediateBuffer, SECBUFFER_TOKEN);
- inputBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- SecBufferDesc inputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(inputBuffers),
- inputBuffers
- };
-
- SecBuffer outBuffers[3];
- outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
- outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
- outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- auto freeBuffers = qScopeGuard([&outBuffers]() {
- for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
- if (outBuffers[i].pvBuffer)
- FreeContextBuffer(outBuffers[i].pvBuffer);
- }
- });
- SecBufferDesc outputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(outBuffers),
- outBuffers
- };
-
- ULONG contextReq = getContextRequirements();
- TimeStamp expiry;
- auto status = InitializeSecurityContext(&credentialHandle, // phCredential
- &contextHandle, // phContext
- const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
- contextReq, // fContextReq
- 0, // Reserved1
- 0, // TargetDataRep (unused)
- &inputBufferDesc, // pInput
- 0, // Reserved2
- nullptr, // phNewContext (we already have one)
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr
- &expiry // ptsExpiry
- );
-
- if (inputBuffers[1].BufferType == SECBUFFER_EXTRA) {
- // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
- // inputBuffers[1].cbBuffer indicates the amount of bytes _NOT_ processed, the rest need to
- // be stored.
- retainExtraData(intermediateBuffer, inputBuffers[1]);
- } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
- // Clear the buffer if we weren't asked for more data
- intermediateBuffer.resize(0);
- }
- switch (status) {
- case SEC_E_OK:
- // Need to transmit a final token in the handshake if 'cbBuffer' is non-zero.
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
- return false;
- schannelState = SchannelState::VerifyHandshake;
- return true;
- case SEC_I_CONTINUE_NEEDED:
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
- return false;
- // Must call InitializeSecurityContext again later (done through continueHandshake)
- return true;
- case SEC_I_INCOMPLETE_CREDENTIALS:
- // Schannel takes care of picking certificate to send (other than the one we can specify),
- // so if we get here then that means we don't have a certificate the server accepts.
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Server did not accept any certificate we could present."));
- return false;
- case SEC_I_CONTEXT_EXPIRED:
- // "The message sender has finished using the connection and has initiated a shutdown."
- if (outBuffers[0].BufferType == SECBUFFER_TOKEN) {
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer))
- return false;
- }
- if (!shutdown) { // we did not initiate this
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- QSslSocket::tr("The TLS/SSL connection has been closed"));
- }
- return true;
- case SEC_E_INCOMPLETE_MESSAGE:
- // Simply incomplete, wait for more data
- missingData = checkIncompleteData(outBuffers[0]);
- return true;
- case SEC_E_ALGORITHM_MISMATCH:
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Algorithm mismatch"));
- shutdown = true; // skip sending the "Shutdown" alert
- return false;
- }
-
- // Note: We can get here if the connection is using TLS 1.2 and the server certificate uses
- // MD5, which is not allowed in Schannel. This causes an "invalid token" error during handshake.
- // (If you came here investigating an error: md5 is insecure, update your certificate)
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Handshake failed: %1").arg(schannelErrorToString(status)));
- return false;
-}
-
-bool QSslSocketBackendPrivate::verifyHandshake()
-{
- Q_Q(QSslSocket);
- sslErrors.clear();
-
- const bool isClient = mode == QSslSocket::SslClientMode;
-#define CHECK_STATUS(status) \
- if (status != SEC_E_OK) { \
- setErrorAndEmit(QAbstractSocket::SslInternalError, \
- QSslSocket::tr("Failed to query the TLS context: %1") \
- .arg(schannelErrorToString(status))); \
- return false; \
- }
-
- // Everything is set up, now make sure there's nothing wrong and query some attributes...
- if (!matchesContextRequirements(contextAttributes, getContextRequirements(),
- configuration.peerVerifyMode, isClient)) {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Did not get the required attributes for the connection."));
- return false;
- }
-
- // Get stream sizes (to know the max size of a message and the size of the header and trailer)
- auto status = QueryContextAttributes(&contextHandle,
- SECPKG_ATTR_STREAM_SIZES,
- &streamSizes);
- CHECK_STATUS(status);
-
- // Get session cipher info
- status = QueryContextAttributes(&contextHandle,
- SECPKG_ATTR_CONNECTION_INFO,
- &connectionInfo);
- CHECK_STATUS(status);
-
-#ifdef SUPPORTS_ALPN
- if (!configuration.nextAllowedProtocols.isEmpty() && supportsAlpn()) {
- SecPkgContext_ApplicationProtocol alpn;
- status = QueryContextAttributes(&contextHandle,
- SECPKG_ATTR_APPLICATION_PROTOCOL,
- &alpn);
- CHECK_STATUS(status);
- if (alpn.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) {
- QByteArray negotiatedProto = QByteArray((const char *)alpn.ProtocolId,
- alpn.ProtocolIdSize);
- if (!configuration.nextAllowedProtocols.contains(negotiatedProto)) {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Unwanted protocol was negotiated"));
- return false;
- }
- configuration.nextNegotiatedProtocol = negotiatedProto;
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
- } else {
- configuration.nextNegotiatedProtocol = "";
- configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationUnsupported;
- }
- }
-#endif // supports ALPN
-
-#undef CHECK_STATUS
-
- // Verify certificate
- CERT_CONTEXT *certificateContext = nullptr;
- auto freeCertificate = qScopeGuard([&certificateContext]() {
- if (certificateContext)
- CertFreeCertificateContext(certificateContext);
- });
- status = QueryContextAttributes(&contextHandle,
- SECPKG_ATTR_REMOTE_CERT_CONTEXT,
- &certificateContext);
-
- // QueryPeer can (currently) not work in Schannel since Schannel itself doesn't have a way to
- // ask for a certificate and then still be OK if it's not received.
- // To work around this we don't request a certificate at all for QueryPeer.
- // For servers AutoVerifyPeer is supposed to be treated the same as QueryPeer.
- // This means that servers using Schannel will only request client certificate for "VerifyPeer".
- if ((!isClient && configuration.peerVerifyMode == QSslSocket::PeerVerifyMode::VerifyPeer)
- || (isClient && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::VerifyNone
- && configuration.peerVerifyMode != QSslSocket::PeerVerifyMode::QueryPeer)) {
- if (status != SEC_E_OK) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Couldn't retrieve peer certificate, status:"
- << schannelErrorToString(status);
-#endif
- const QSslError error{ QSslError::NoPeerCertificate };
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
-
- // verifyCertContext returns false if the user disconnected while it was checking errors.
- if (certificateContext && !verifyCertContext(certificateContext))
- return false;
-
- if (!checkSslErrors() || state != QAbstractSocket::ConnectedState) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << __func__ << "was unsuccessful. Paused:" << paused;
-#endif
- // If we're paused then checkSslErrors returned false, but it's not an error
- return paused && state == QAbstractSocket::ConnectedState;
- }
-
- schannelState = SchannelState::Done;
- return true;
-}
-
-bool QSslSocketBackendPrivate::renegotiate()
-{
- SecBuffer outBuffers[3];
- outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
- outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
- outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- auto freeBuffers = qScopeGuard([&outBuffers]() {
- for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
- if (outBuffers[i].pvBuffer)
- FreeContextBuffer(outBuffers[i].pvBuffer);
- }
- });
- SecBufferDesc outputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(outBuffers),
- outBuffers
- };
-
- ULONG contextReq = getContextRequirements();
- TimeStamp expiry;
- SECURITY_STATUS status;
- if (mode == QSslSocket::SslClientMode) {
- status = InitializeSecurityContext(&credentialHandle, // phCredential
- &contextHandle, // phContext
- const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
- contextReq, // fContextReq
- 0, // Reserved1
- 0, // TargetDataRep (unused)
- nullptr, // pInput (nullptr for renegotiate)
- 0, // Reserved2
- nullptr, // phNewContext (we already have one)
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr
- &expiry // ptsExpiry
- );
- } else {
- status = AcceptSecurityContext(
- &credentialHandle, // phCredential
- &contextHandle, // phContext
- nullptr, // pInput
- contextReq, // fContextReq
- 0, // TargetDataRep (unused)
- nullptr, // phNewContext
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr,
- &expiry // ptsTimeStamp
- );
- }
- if (status == SEC_I_CONTINUE_NEEDED) {
- schannelState = SchannelState::PerformHandshake;
- return sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
- }
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- QSslSocket::tr("Renegotiation was unsuccessful: %1").arg(schannelErrorToString(status)));
- return false;
-}
-
-/*!
- \internal
- reset the state in preparation for reuse of socket
-*/
-void QSslSocketBackendPrivate::reset()
-{
- closeCertificateStores(); // certificate stores could've changed
- deallocateContext();
- freeCredentialsHandle(); // in case we already had one (@future: session resumption requires re-use)
-
- connectionInfo = {};
- streamSizes = {};
-
- CertFreeCertificateContext(localCertContext);
- localCertContext = nullptr;
-
- contextAttributes = 0;
- intermediateBuffer.clear();
- schannelState = SchannelState::InitializeHandshake;
-
- connectionEncrypted = false;
- shutdown = false;
- renegotiating = false;
-
- missingData = 0;
-}
-
-void QSslSocketBackendPrivate::startClientEncryption()
-{
- if (connectionEncrypted)
- return; // let's not mess up the connection...
- reset();
- continueHandshake();
-}
-
-void QSslSocketBackendPrivate::startServerEncryption()
-{
- if (connectionEncrypted)
- return; // let's not mess up the connection...
- reset();
- continueHandshake();
-}
-
-void QSslSocketBackendPrivate::transmit()
-{
- Q_Q(QSslSocket);
-
- // Can happen if called through QSslSocket::abort->QSslSocket::close->QSslSocket::flush->here
- if (plainSocket->state() == QAbstractSocket::SocketState::UnconnectedState)
- return;
-
- if (schannelState != SchannelState::Done) {
- continueHandshake();
- return;
- }
-
- if (connectionEncrypted) { // encrypt data in writeBuffer and write it to plainSocket
- qint64 totalBytesWritten = 0;
- qint64 writeBufferSize;
- while ((writeBufferSize = writeBuffer.size()) > 0) {
- const int headerSize = int(streamSizes.cbHeader);
- const int trailerSize = int(streamSizes.cbTrailer);
- // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
- const int size = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
- QByteArray fullMessage(headerSize + trailerSize + size, Qt::Uninitialized);
- {
- // Use peek() here instead of read() so we don't lose data if encryption fails.
- qint64 copied = writeBuffer.peek(fullMessage.data() + headerSize, size);
- Q_ASSERT(copied == size);
- }
-
- SecBuffer inputBuffers[4]{
- createSecBuffer(fullMessage.data(), headerSize, SECBUFFER_STREAM_HEADER),
- createSecBuffer(fullMessage.data() + headerSize, size, SECBUFFER_DATA),
- createSecBuffer(fullMessage.data() + headerSize + size, trailerSize, SECBUFFER_STREAM_TRAILER),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
- };
- SecBufferDesc message{
- SECBUFFER_VERSION,
- ARRAYSIZE(inputBuffers),
- inputBuffers
- };
- auto status = EncryptMessage(&contextHandle, 0, &message, 0);
- if (status != SEC_E_OK) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Schannel failed to encrypt data: %1")
- .arg(schannelErrorToString(status)));
- return;
- }
- // Data was encrypted successfully, so we free() what we peek()ed earlier
- writeBuffer.free(size);
-
- // The trailer's size is not final, so resize fullMessage to not send trailing junk
- fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer + inputBuffers[2].cbBuffer);
- const qint64 bytesWritten = plainSocket->write(fullMessage);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Wrote %lld of total %d bytes", bytesWritten, fullMessage.length());
-#endif
- if (bytesWritten >= 0) {
- totalBytesWritten += bytesWritten;
- } else {
- setErrorAndEmit(plainSocket->error(), plainSocket->errorString());
- return;
- }
- }
-
- if (totalBytesWritten > 0) {
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- emittedBytesWritten = true;
- emit q->bytesWritten(totalBytesWritten);
- emittedBytesWritten = false;
- }
- emit q->channelBytesWritten(0, totalBytesWritten);
- }
- }
-
- if (connectionEncrypted) { // Decrypt data from remote
- int totalRead = 0;
- bool hadIncompleteData = false;
- while (!readBufferMaxSize || buffer.size() < readBufferMaxSize) {
- if (missingData > plainSocket->bytesAvailable()) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "We're still missing %lld bytes, will check later.", missingData);
-#endif
- break;
- }
-
- missingData = 0;
- const qint64 bytesRead = readToBuffer(intermediateBuffer, plainSocket);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Read %lld encrypted bytes from the socket", bytesRead);
-#endif
- if (intermediateBuffer.length() == 0 || (hadIncompleteData && bytesRead == 0)) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, (hadIncompleteData ? "No new data received, leaving loop!"
- : "Nothing to decrypt, leaving loop!"));
-#endif
- break;
- }
- hadIncompleteData = false;
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Total amount of bytes to decrypt: %d", intermediateBuffer.length());
-#endif
-
- SecBuffer dataBuffer[4]{
- createSecBuffer(intermediateBuffer, SECBUFFER_DATA),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY),
- createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
- };
- SecBufferDesc message{
- SECBUFFER_VERSION,
- ARRAYSIZE(dataBuffer),
- dataBuffer
- };
- auto status = DecryptMessage(&contextHandle, &message, 0, nullptr);
- if (status == SEC_E_OK || status == SEC_I_RENEGOTIATE || status == SEC_I_CONTEXT_EXPIRED) {
- // There can still be 0 output even if it succeeds, this is fine
- if (dataBuffer[1].cbBuffer > 0) {
- // It is always decrypted in-place.
- // But [0] is the STREAM_HEADER, [1] is the DATA and [2] is the STREAM_TRAILER.
- // The pointers in all of those still point into 'intermediateBuffer'.
- buffer.append(static_cast<char *>(dataBuffer[1].pvBuffer),
- dataBuffer[1].cbBuffer);
- totalRead += dataBuffer[1].cbBuffer;
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "Decrypted %lu bytes. New read buffer size: %d",
- dataBuffer[1].cbBuffer, buffer.size());
-#endif
- }
- if (dataBuffer[3].BufferType == SECBUFFER_EXTRA) {
- // https://docs.microsoft.com/en-us/windows/desktop/secauthn/extra-buffers-returned-by-schannel
- // dataBuffer[3].cbBuffer indicates the amount of bytes _NOT_ processed,
- // the rest need to be stored.
- retainExtraData(intermediateBuffer, dataBuffer[3]);
- } else {
- intermediateBuffer.resize(0);
- }
- }
-
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- missingData = checkIncompleteData(dataBuffer[0]);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "We didn't have enough data to decrypt anything, will try again!");
-#endif
- // We try again, but if we don't get any more data then we leave
- hadIncompleteData = true;
- } else if (status == SEC_E_INVALID_HANDLE) {
- // I don't think this should happen, if it does we're done...
- qCWarning(lcSsl, "The internal SSPI handle is invalid!");
- Q_UNREACHABLE();
- } else if (status == SEC_E_INVALID_TOKEN) {
- qCWarning(lcSsl, "Got SEC_E_INVALID_TOKEN!");
- Q_UNREACHABLE(); // Happened once due to a bug, but shouldn't generally happen(?)
- } else if (status == SEC_E_MESSAGE_ALTERED) {
- // The message has been altered, disconnect now.
- shutdown = true; // skips sending the shutdown alert
- disconnectFromHost();
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_E_OUT_OF_SEQUENCE) {
- // @todo: I don't know if this one is actually "fatal"..
- // This path might never be hit as it seems this is for connection-oriented connections,
- // while SEC_E_MESSAGE_ALTERED is for stream-oriented ones (what we use).
- shutdown = true; // skips sending the shutdown alert
- disconnectFromHost();
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_I_CONTEXT_EXPIRED) {
- // 'remote' has initiated a shutdown
- disconnectFromHost();
- setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
- schannelErrorToString(status));
- break;
- } else if (status == SEC_I_RENEGOTIATE) {
- // 'remote' wants to renegotiate
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "The peer wants to renegotiate.");
-#endif
- schannelState = SchannelState::Renegotiate;
- renegotiating = true;
-
- // We need to call 'continueHandshake' or else there's no guarantee it ever gets called
- continueHandshake();
- break;
- }
- }
-
- if (totalRead) {
- if (readyReadEmittedPointer)
- *readyReadEmittedPointer = true;
- emit q->readyRead();
- emit q->channelReadyRead(0);
- }
- }
-}
-
-void QSslSocketBackendPrivate::sendShutdown()
-{
- const bool isClient = mode == QSslSocket::SslClientMode;
- DWORD shutdownToken = SCHANNEL_SHUTDOWN;
- SecBuffer buffer = createSecBuffer(&shutdownToken, sizeof(SCHANNEL_SHUTDOWN), SECBUFFER_TOKEN);
- SecBufferDesc token{
- SECBUFFER_VERSION,
- 1,
- &buffer
- };
- auto status = ApplyControlToken(&contextHandle, &token);
-
- if (status != SEC_E_OK) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Failed to apply shutdown control token:" << schannelErrorToString(status);
-#endif
- return;
- }
-
- SecBuffer outBuffers[3];
- outBuffers[0] = createSecBuffer(nullptr, 0, SECBUFFER_TOKEN);
- outBuffers[1] = createSecBuffer(nullptr, 0, SECBUFFER_ALERT);
- outBuffers[2] = createSecBuffer(nullptr, 0, SECBUFFER_EMPTY);
- auto freeBuffers = qScopeGuard([&outBuffers]() {
- for (auto i = 0ull; i < ARRAYSIZE(outBuffers); i++) {
- if (outBuffers[i].pvBuffer)
- FreeContextBuffer(outBuffers[i].pvBuffer);
- }
- });
- SecBufferDesc outputBufferDesc{
- SECBUFFER_VERSION,
- ARRAYSIZE(outBuffers),
- outBuffers
- };
-
- ULONG contextReq = getContextRequirements();
- TimeStamp expiry;
- if (isClient) {
- status = InitializeSecurityContext(&credentialHandle, // phCredential
- &contextHandle, // phContext
- const_reinterpret_cast<SEC_WCHAR *>(targetName().utf16()), // pszTargetName
- contextReq, // fContextReq
- 0, // Reserved1
- 0, // TargetDataRep (unused)
- nullptr, // pInput
- 0, // Reserved2
- nullptr, // phNewContext (we already have one)
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr
- &expiry // ptsExpiry
- );
- } else {
- status = AcceptSecurityContext(
- &credentialHandle, // phCredential
- &contextHandle, // phContext
- nullptr, // pInput
- contextReq, // fContextReq
- 0, // TargetDataRep (unused)
- nullptr, // phNewContext
- &outputBufferDesc, // pOutput
- &contextAttributes, // pfContextAttr,
- &expiry // ptsTimeStamp
- );
- }
- if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED) {
- if (!sendToken(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, false)) {
- // We failed to send the shutdown message, but it's not that important since we're
- // shutting down anyway.
- return;
- }
- } else {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "Failed to initialize shutdown:" << schannelErrorToString(status);
-#endif
- }
-}
-
-void QSslSocketBackendPrivate::disconnectFromHost()
-{
- if (SecIsValidHandle(&contextHandle)) {
- if (!shutdown) {
- shutdown = true;
- if (plainSocket->state() != QAbstractSocket::UnconnectedState) {
- if (connectionEncrypted) {
- // Read as much as possible because this is likely our last chance
- qint64 tempMax = readBufferMaxSize;
- readBufferMaxSize = 0;
- transmit();
- readBufferMaxSize = tempMax;
- sendShutdown();
- }
- }
- }
- }
- if (plainSocket->state() != QAbstractSocket::UnconnectedState)
- plainSocket->disconnectFromHost();
-}
-
-void QSslSocketBackendPrivate::disconnected()
-{
- shutdown = true;
- connectionEncrypted = false;
- deallocateContext();
- freeCredentialsHandle();
-}
-
-QSslCipher QSslSocketBackendPrivate::sessionCipher() const
-{
- if (!connectionEncrypted)
- return QSslCipher();
- return QSslCipher(QStringLiteral("Schannel"), sessionProtocol());
-}
-
-QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
-{
- if (!connectionEncrypted)
- return QSsl::SslProtocol::UnknownProtocol;
- return toQtSslProtocol(connectionInfo.dwProtocol);
-}
-
-void QSslSocketBackendPrivate::continueHandshake()
-{
- Q_Q(QSslSocket);
- const bool isServer = mode == QSslSocket::SslServerMode;
- switch (schannelState) {
- case SchannelState::InitializeHandshake:
- if (!SecIsValidHandle(&credentialHandle) && !acquireCredentialsHandle()) {
- disconnectFromHost();
- return;
- }
- if (!SecIsValidHandle(&credentialHandle)) // Needed to support tst_QSslSocket::setEmptyKey
- return;
- if (!SecIsValidHandle(&contextHandle) && !(isServer ? acceptContext() : createContext())) {
- disconnectFromHost();
- return;
- }
- if (schannelState != SchannelState::PerformHandshake)
- break;
- Q_FALLTHROUGH();
- case SchannelState::PerformHandshake:
- if (!performHandshake()) {
- disconnectFromHost();
- return;
- }
- if (schannelState != SchannelState::VerifyHandshake)
- break;
- Q_FALLTHROUGH();
- case SchannelState::VerifyHandshake:
- // if we're in shutdown or renegotiating then we might not need to verify
- // (since we already did)
- if (!verifyHandshake()) {
- shutdown = true; // Skip sending shutdown alert
- q->abort(); // We don't want to send buffered data
- disconnectFromHost();
- return;
- }
- if (schannelState != SchannelState::Done)
- break;
- Q_FALLTHROUGH();
- case SchannelState::Done:
- // connectionEncrypted is already true if we come here from a renegotiation
- if (!connectionEncrypted) {
- connectionEncrypted = true; // all is done
- emit q->encrypted();
- }
- renegotiating = false;
- if (pendingClose) {
- pendingClose = false;
- disconnectFromHost();
- } else {
- transmit();
- }
- break;
- case SchannelState::Renegotiate:
- if (!renegotiate()) {
- disconnectFromHost();
- return;
- }
- break;
- }
-}
-
-QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
-{
- QList<QSslCipher> ciphers;
- // @temp (I hope), stolen from qsslsocket_winrt.cpp
- const QString protocolStrings[] = { QStringLiteral("TLSv1"), QStringLiteral("TLSv1.1"),
- QStringLiteral("TLSv1.2"), QStringLiteral("TLSv1.3") };
- const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1,
- QSsl::TlsV1_2, QSsl::TlsV1_3 };
- const int size = ARRAYSIZE(protocols);
- Q_STATIC_ASSERT(size == ARRAYSIZE(protocolStrings));
- ciphers.reserve(size);
- for (int i = 0; i < size; ++i) {
- QSslCipher cipher;
- cipher.d->isNull = false;
- cipher.d->name = QStringLiteral("Schannel");
- cipher.d->protocol = protocols[i];
- cipher.d->protocolString = protocolStrings[i];
- ciphers.append(cipher);
- }
-
- return ciphers;
-}
-
-QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain,
- const QString &hostName)
-{
- Q_UNUSED(certificateChain);
- Q_UNUSED(hostName);
-
- Q_UNIMPLEMENTED();
- return {}; // @future implement(?)
-}
-
-bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase)
-{
- Q_UNUSED(device);
- Q_UNUSED(key);
- Q_UNUSED(cert);
- Q_UNUSED(caCertificates);
- Q_UNUSED(passPhrase);
- // @future: can load into its own certificate store (encountered problems extracting key).
- Q_UNIMPLEMENTED();
- return false;
-}
-
-/*
- Copied from qsslsocket_mac.cpp, which was copied from qsslsocket_openssl.cpp
-*/
-bool QSslSocketBackendPrivate::checkSslErrors()
-{
- if (sslErrors.isEmpty())
- return true;
- Q_Q(QSslSocket);
-
- emit q->sslErrors(sslErrors);
-
- const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
- const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
- // check whether we need to emit an SSL handshake error
- if (doVerifyPeer && doEmitSslError) {
- if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
- pauseSocketNotifiers(q);
- paused = true;
- } else {
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError,
- sslErrors.constFirst().errorString());
- plainSocket->disconnectFromHost();
- }
- return false;
- }
-
- return true;
-}
-
-void QSslSocketBackendPrivate::initializeCertificateStores()
-{
- //// helper function which turns a chain into a certificate store
- auto createStoreFromCertificateChain = [](const QList<QSslCertificate> certChain, const QSslKey &privateKey) {
- const wchar_t *passphrase = L"";
- // Need to embed the private key in the certificate
- QByteArray pkcs12 = _q_makePkcs12(certChain,
- privateKey,
- QString::fromWCharArray(passphrase, 0));
- CRYPT_DATA_BLOB pfxBlob;
- pfxBlob.cbData = DWORD(pkcs12.length());
- pfxBlob.pbData = reinterpret_cast<unsigned char *>(pkcs12.data());
- return QHCertStorePointer(PFXImportCertStore(&pfxBlob, passphrase, 0));
- };
-
- if (!configuration.localCertificateChain.isEmpty()) {
- if (configuration.privateKey.isNull()) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QSslSocket::tr("Cannot provide a certificate with no key"));
- return;
- }
- if (localCertificateStore == nullptr) {
- localCertificateStore = createStoreFromCertificateChain(configuration.localCertificateChain,
- configuration.privateKey);
- if (localCertificateStore == nullptr)
- qCWarning(lcSsl, "Failed to load certificate chain!");
- }
- }
-
- if (!configuration.caCertificates.isEmpty() && !caCertificateStore) {
- caCertificateStore = createStoreFromCertificateChain(configuration.caCertificates,
- {}); // No private key for the CA certs
- }
-}
-
-bool QSslSocketBackendPrivate::verifyCertContext(CERT_CONTEXT *certContext)
-{
- Q_ASSERT(certContext);
- Q_Q(QSslSocket);
-
- const bool isClient = mode == QSslSocket::SslClientMode;
-
- // Create a collection of stores so we can pass in multiple stores as additional locations to
- // search for the certificate chain
- auto tempCertCollection = QHCertStorePointer(CertOpenStore(CERT_STORE_PROV_COLLECTION,
- X509_ASN_ENCODING,
- 0,
- CERT_STORE_CREATE_NEW_FLAG,
- nullptr));
- if (!tempCertCollection) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl, "Failed to create certificate store collection!");
-#endif
- return false;
- }
-
- if (rootCertOnDemandLoadingAllowed()) {
- // @future(maybe): following the OpenSSL backend these certificates should be added into
- // the Ca list, not just included during verification.
- // That being said, it's not trivial to add the root certificates (if and only if they
- // came from the system root store). And I don't see this mentioned in our documentation.
- auto rootStore = QHCertStorePointer(CertOpenSystemStore(0, L"ROOT"));
- if (!rootStore) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl, "Failed to open the system root CA certificate store!");
-#endif
- return false;
- } else if (!CertAddStoreToCollection(tempCertCollection.get(), rootStore.get(), 0, 1)) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl, "Failed to add the system root CA certificate store to the certificate store collection!");
-#endif
- return false;
- }
- }
- if (caCertificateStore) {
- if (!CertAddStoreToCollection(tempCertCollection.get(), caCertificateStore.get(), 0, 1)) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl, "Failed to add the user's CA certificate store to the certificate store collection!");
-#endif
- return false;
- }
- }
-
- if (!CertAddStoreToCollection(tempCertCollection.get(), certContext->hCertStore, 0, 0)) {
-#ifdef QSSLSOCKET_DEBUG
- qCWarning(lcSsl, "Failed to add certificate's origin store to the certificate store collection!");
-#endif
- return false;
- }
-
- CERT_CHAIN_PARA parameters;
- ZeroMemory(&parameters, sizeof(parameters));
- parameters.cbSize = sizeof(CERT_CHAIN_PARA);
- parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
- parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
- LPSTR oid = LPSTR(isClient ? szOID_PKIX_KP_SERVER_AUTH
- : szOID_PKIX_KP_CLIENT_AUTH);
- parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
-
- configuration.peerCertificate.clear();
- configuration.peerCertificateChain.clear();
- const CERT_CHAIN_CONTEXT *chainContext = nullptr;
- auto freeCertChain = qScopeGuard([&chainContext]() {
- if (chainContext)
- CertFreeCertificateChain(chainContext);
- });
- BOOL status = CertGetCertificateChain(nullptr, // hChainEngine, default
- certContext, // pCertContext
- nullptr, // pTime, 'now'
- tempCertCollection.get(), // hAdditionalStore, additional cert store
- &parameters, // pChainPara
- CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // dwFlags
- nullptr, // reserved
- &chainContext // ppChainContext
- );
- if (status == FALSE || !chainContext || chainContext->cChain == 0) {
- QSslError error(QSslError::UnableToVerifyFirstCertificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- return q->state() == QAbstractSocket::ConnectedState;
- }
-
- // Helper-function to get a QSslCertificate given a CERT_CHAIN_ELEMENT
- static auto getCertificateFromChainElement = [](CERT_CHAIN_ELEMENT *element) {
- if (!element)
- return QSslCertificate();
-
- const CERT_CONTEXT *certContext = element->pCertContext;
- return QSslCertificatePrivate::QSslCertificate_from_CERT_CONTEXT(certContext);
- };
-
- // Pick a chain to use as the certificate chain, if multiple are available:
- // According to https://docs.microsoft.com/en-gb/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
- // this seems to be the best way to get a trusted chain.
- CERT_SIMPLE_CHAIN *chain = chainContext->rgpChain[chainContext->cChain - 1];
-
- if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) {
- auto error = QSslError(QSslError::SslError::UnableToGetIssuerCertificate,
- getCertificateFromChainElement(chain->rgpElement[chain->cElement - 1]));
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
- // @Note: This is actually one of two errors:
- // "either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded."
- // But here we are checking the chain's status, so we assume the "issuing" error cannot occur here.
- auto error = QSslError(QSslError::PathLengthExceeded);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- static const DWORD leftoverCertChainErrorMask = CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_EXTENSION
- | CERT_TRUST_INVALID_POLICY_CONSTRAINTS | CERT_TRUST_INVALID_NAME_CONSTRAINTS
- | CERT_TRUST_CTL_IS_NOT_TIME_VALID | CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID
- | CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
- if (chain->TrustStatus.dwErrorStatus & leftoverCertChainErrorMask) {
- auto error = QSslError(QSslError::SslError::UnspecifiedError);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- DWORD verifyDepth = chain->cElement;
- if (configuration.peerVerifyDepth > 0 && DWORD(configuration.peerVerifyDepth) < verifyDepth)
- verifyDepth = DWORD(configuration.peerVerifyDepth);
-
- for (DWORD i = 0; i < verifyDepth; i++) {
- CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
- QSslCertificate certificate = getCertificateFromChainElement(element);
- const QList<QSslCertificateExtension> extensions = certificate.extensions();
-
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "issuer:" << certificate.issuerDisplayName()
- << "\nsubject:" << certificate.subjectDisplayName()
- << "\nQSslCertificate info:" << certificate
- << "\nextended error info:" << element->pwszExtendedErrorInfo
- << "\nerror status:" << element->TrustStatus.dwErrorStatus;
-#endif
-
- configuration.peerCertificateChain.append(certificate);
-
- if (certificate.isBlacklisted()) {
- const auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- LONG result = CertVerifyTimeValidity(nullptr /*== now */, element->pCertContext->pCertInfo);
- if (result != 0) {
- auto error = QSslError(result == -1 ? QSslError::CertificateNotYetValid
- : QSslError::CertificateExpired,
- certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- //// Errors
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) {
- // handled right above
- Q_ASSERT(!sslErrors.isEmpty());
- }
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) {
- auto error = QSslError(QSslError::CertificateRevoked, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
- auto error = QSslError(QSslError::CertificateSignatureFailed, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- // While netscape shouldn't be relevant now it defined an extension which is
- // still in use. Schannel does not check this automatically, so we do it here.
- // It is used to differentiate between client and server certificates.
- if (netscapeWrongCertType(extensions, isClient))
- element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
-
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
- auto error = QSslError(QSslError::InvalidPurpose, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT) {
- // Override this error if we have the certificate inside our trusted CAs list.
- const bool isTrustedRoot = configuration.caCertificates.contains(certificate);
- if (!isTrustedRoot) {
- auto error = QSslError(QSslError::CertificateUntrusted, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- static const DWORD certRevocationCheckUnavailableError = CERT_TRUST_IS_OFFLINE_REVOCATION
- | CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
- if (element->TrustStatus.dwErrorStatus & certRevocationCheckUnavailableError) {
- // @future(maybe): Do something with this
- }
-
- // Dumping ground of errors that don't fit our specific errors
- static const DWORD leftoverCertErrorMask = CERT_TRUST_IS_CYCLIC
- | CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS
- | CERT_TRUST_INVALID_POLICY_CONSTRAINTS
- | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT
- | CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT
- | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT
- | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT
- | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
- if (element->TrustStatus.dwErrorStatus & leftoverCertErrorMask) {
- auto error = QSslError(QSslError::UnspecifiedError, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
- auto it = std::find_if(extensions.cbegin(), extensions.cend(),
- [](const QSslCertificateExtension &extension) {
- return extension.name() == QLatin1String("basicConstraints");
- });
- if (it != extensions.cend()) {
- // @Note: This is actually one of two errors:
- // "either the certificate cannot be used to issue other certificates,
- // or the chain path length has been exceeded."
- QVariantMap basicConstraints = it->value().toMap();
- QSslError error;
- if (i > 0 && !basicConstraints.value(QLatin1String("ca"), false).toBool())
- error = QSslError(QSslError::InvalidPurpose, certificate);
- else
- error = QSslError(QSslError::PathLengthExceeded, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- if (element->TrustStatus.dwErrorStatus & CERT_TRUST_IS_EXPLICIT_DISTRUST) {
- auto error = QSslError(QSslError::CertificateBlacklisted, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- if (element->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) {
- // If it's self-signed *and* a CA then we can assume it's a root CA certificate
- // and we can ignore the "self-signed" note:
- // We check the basicConstraints certificate extension when possible, but this didn't
- // exist for version 1, so we can only guess in that case
- const bool isRootCertificateAuthority = isCertificateAuthority(extensions)
- || certificate.version() == "1";
-
- // Root certificate tends to be signed by themselves, so ignore self-signed status.
- if (!isRootCertificateAuthority) {
- auto error = QSslError(QSslError::SelfSignedCertificate, certificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- }
-
- if (!configuration.peerCertificateChain.isEmpty())
- configuration.peerCertificate = configuration.peerCertificateChain.first();
-
- // @Note: Somewhat copied from qsslsocket_mac.cpp
- const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
- && mode == QSslSocket::SslClientMode);
- // Check the peer certificate itself. First try the subject's common name
- // (CN) as a wildcard, then try all alternate subject name DNS entries the
- // same way.
- if (!configuration.peerCertificate.isNull()) {
- // but only if we're a client connecting to a server
- // if we're the server, don't check CN
- if (mode == QSslSocket::SslClientMode) {
- const QString peerName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
- if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
- // No matches in common names or alternate names.
- const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
- }
- } else if (doVerifyPeer) {
- // No peer certificate presented. Report as error if the socket
- // expected one.
- const QSslError error(QSslError::NoPeerCertificate);
- sslErrors += error;
- emit q->peerVerifyError(error);
- if (q->state() != QAbstractSocket::ConnectedState)
- return false;
- }
-
- return true;
-}
-
-bool QSslSocketBackendPrivate::rootCertOnDemandLoadingAllowed()
-{
- return allowRootCertOnDemandLoading && s_loadRootCertsOnDemand;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_schannel_p.h b/src/network/ssl/qsslsocket_schannel_p.h
deleted file mode 100644
index a184deef49..0000000000
--- a/src/network/ssl/qsslsocket_schannel_p.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_SCHANNEL_P_H
-#define QSSLSOCKET_SCHANNEL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-QT_REQUIRE_CONFIG(schannel);
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-
-#include "qsslsocket_p.h"
-
-#define SECURITY_WIN32
-#include <security.h>
-#include <schnlsp.h>
-#undef SECURITY_WIN32
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-
-struct QHCertStoreDeleter {
- void operator()(HCERTSTORE store)
- {
- CertCloseStore(store, 0);
- }
-};
-typedef std::unique_ptr<void, QHCertStoreDeleter> QHCertStorePointer;
-
-class QSslSocketBackendPrivate final : public QSslSocketPrivate
-{
- Q_DISABLE_COPY_MOVE(QSslSocketBackendPrivate)
- Q_DECLARE_PUBLIC(QSslSocket)
-public:
- QSslSocketBackendPrivate();
- ~QSslSocketBackendPrivate();
-
- // Platform specific functions
- void startClientEncryption() override;
- void startServerEncryption() override;
- void transmit() override;
- void disconnectFromHost() override;
- void disconnected() override;
- QSslCipher sessionCipher() const override;
- QSsl::SslProtocol sessionProtocol() const override;
- void continueHandshake() override;
-
- static QList<QSslCipher> defaultCiphers();
- static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain,
- const QString &hostName);
- static bool importPkcs12(QIODevice *device, QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates, const QByteArray &passPhrase);
-
-private:
- enum class SchannelState {
- InitializeHandshake, // create and transmit context (client)/accept context (server)
- PerformHandshake, // get token back, process it
- VerifyHandshake, // Verify that things are OK
- Done, // Connection encrypted!
- Renegotiate // Renegotiating!
- } schannelState = SchannelState::InitializeHandshake;
-
- void reset();
- bool acquireCredentialsHandle();
- ULONG getContextRequirements();
- bool createContext(); // for clients
- bool acceptContext(); // for server
- bool performHandshake();
- bool verifyHandshake();
- bool renegotiate();
-
- bool sendToken(void *token, unsigned long tokenLength, bool emitError = true);
- QString targetName() const;
-
- bool checkSslErrors();
- void deallocateContext();
- void freeCredentialsHandle();
- void closeCertificateStores();
- void sendShutdown();
-
- void initializeCertificateStores();
- bool verifyCertContext(CERT_CONTEXT *certContext);
-
- bool rootCertOnDemandLoadingAllowed();
-
- SecPkgContext_ConnectionInfo connectionInfo = {};
- SecPkgContext_StreamSizes streamSizes = {};
-
- CredHandle credentialHandle; // Initialized in ctor
- CtxtHandle contextHandle; // Initialized in ctor
-
- QByteArray intermediateBuffer; // data which is left-over or incomplete
-
- QHCertStorePointer localCertificateStore = nullptr;
- QHCertStorePointer peerCertificateStore = nullptr;
- QHCertStorePointer caCertificateStore = nullptr;
-
- const CERT_CONTEXT *localCertContext = nullptr;
-
- ULONG contextAttributes = 0;
- qint64 missingData = 0;
-
- bool renegotiating = false;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSSLSOCKET_SCHANNEL_P_H
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
deleted file mode 100644
index f3ca3dc257..0000000000
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ /dev/null
@@ -1,688 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qssl_p.h"
-#include "qsslsocket_winrt_p.h"
-#include "qsslsocket.h"
-#include "qsslcertificate_p.h"
-#include "qsslcipher_p.h"
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QSysInfo>
-#include <QtCore/qfunctions_winrt.h>
-#include <private/qnativesocketengine_winrt_p.h>
-#include <private/qeventdispatcher_winrt_p.h>
-
-#include <windows.networking.h>
-#include <windows.networking.sockets.h>
-#include <windows.security.cryptography.certificates.h>
-#include <robuffer.h>
-
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace ABI::Windows::Foundation;
-using namespace ABI::Windows::Foundation::Collections;
-using namespace ABI::Windows::Networking;
-using namespace ABI::Windows::Networking::Sockets;
-using namespace ABI::Windows::Security::Cryptography::Certificates;
-using namespace ABI::Windows::Storage::Streams;
-
-QT_BEGIN_NAMESPACE
-
-bool QSslSocketPrivate::s_libraryLoaded = true;
-bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
-bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
-
-struct SslSocketGlobal
-{
- SslSocketGlobal()
- {
- HRESULT hr;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
- &hostNameFactory);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<ICertificateStoresStatics> certificateStores;
- hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Certificates_CertificateStores).Get(),
- &certificateStores);
- Q_ASSERT_SUCCEEDED(hr);
-
- hr = certificateStores->get_TrustedRootCertificationAuthorities(&rootStore);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IAsyncOperation<IVectorView<Certificate *> *>> op;
- hr = certificateStores->FindAllAsync(&op);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IVectorView<Certificate *>> certificates;
- hr = QWinRTFunctions::await(op, certificates.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
- quint32 size;
- hr = certificates->get_Size(&size);
- Q_ASSERT_SUCCEEDED(hr);
- for (quint32 i = 0; i < size; ++i) {
- ComPtr<ICertificate> certificate;
- hr = certificates->GetAt(i, &certificate);
- Q_ASSERT_SUCCEEDED(hr);
- systemCaCertificates.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get()));
- }
- }
-
- void syncCaCertificates(const QSet<QSslCertificate> &add, const QSet<QSslCertificate> &remove)
- {
- QMutexLocker locker(&certificateMutex);
- for (const QSslCertificate &certificate : add) {
- QHash<QSslCertificate, QAtomicInt>::iterator it = additionalCertificates.find(certificate);
- if (it != additionalCertificates.end()) {
- it.value().ref(); // Add a reference
- } else {
- // install certificate
- HRESULT hr;
- hr = rootStore->Add(static_cast<ICertificate *>(certificate.handle()));
- Q_ASSERT_SUCCEEDED(hr);
- additionalCertificates.insert(certificate, 1);
- }
- }
- for (const QSslCertificate &certificate : remove) {
- QHash<QSslCertificate, QAtomicInt>::iterator it = additionalCertificates.find(certificate);
- if (it != additionalCertificates.end() && !it.value().deref()) {
- // no more references, remove certificate
- HRESULT hr;
- hr = rootStore->Delete(static_cast<ICertificate *>(certificate.handle()));
- Q_ASSERT_SUCCEEDED(hr);
- additionalCertificates.erase(it);
- }
- }
- }
-
- ComPtr<IHostNameFactory> hostNameFactory;
- QList<QSslCertificate> systemCaCertificates;
-
-private:
- QMutex certificateMutex;
- ComPtr<ICertificateStore> rootStore;
- QHash<QSslCertificate, QAtomicInt> additionalCertificates;
-};
-Q_GLOBAL_STATIC(SslSocketGlobal, g)
-
-// Called on the socket's thread to avoid cross-thread deletion
-void QSslSocketConnectionHelper::disconnectSocketFromHost()
-{
- if (d->plainSocket)
- d->plainSocket->disconnectFromHost();
-}
-
-QSslSocketBackendPrivate::QSslSocketBackendPrivate()
- : connectionHelper(new QSslSocketConnectionHelper(this))
-{
-}
-
-QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
-{
- g->syncCaCertificates(QSet<QSslCertificate>(), previousCaCertificates);
-}
-
-bool QSslSocketPrivate::supportsSsl()
-{
- return true;
-}
-
-void QSslSocketPrivate::ensureInitialized()
-{
- if (s_loadedCiphersAndCerts)
- return;
- s_loadedCiphersAndCerts = true;
- resetDefaultCiphers();
-}
-
-long QSslSocketPrivate::sslLibraryVersionNumber()
-{
- // ### Qt 6: Find a proper replacement for the deprecated method below.
- return QSysInfo::windowsVersion();
-}
-
-QString QSslSocketPrivate::sslLibraryVersionString()
-{
- return QStringLiteral("Windows Runtime, ") + QSysInfo::prettyProductName();
-}
-
-long QSslSocketPrivate::sslLibraryBuildVersionNumber()
-{
- Q_UNIMPLEMENTED();
- return 0;
-}
-
-QString QSslSocketPrivate::sslLibraryBuildVersionString()
-{
- Q_UNIMPLEMENTED();
- return QString::number(sslLibraryBuildVersionNumber());
-}
-
-void QSslSocketPrivate::resetDefaultCiphers()
-{
- setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers());
- setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers());
-}
-
-
-QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
-{
- QList<QSslCipher> ciphers;
- const QString protocolStrings[] = { QStringLiteral("TLSv1"),
- QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") };
- const QSsl::SslProtocol protocols[] = { QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 };
- const int size = static_cast<int>(ARRAYSIZE(protocols));
- ciphers.reserve(size);
- for (int i = 0; i < size; ++i) {
- QSslCipher cipher;
- cipher.d->isNull = false;
- cipher.d->name = QStringLiteral("WINRT");
- cipher.d->protocol = protocols[i];
- cipher.d->protocolString = protocolStrings[i];
- ciphers.append(cipher);
- }
- return ciphers;
-}
-
-QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
-{
- return g->systemCaCertificates;
-}
-
-void QSslSocketBackendPrivate::startClientEncryption()
-{
- Q_Q(QSslSocket);
-
- QSsl::SslProtocol protocol = q->protocol();
- switch (q->protocol()) {
- case QSsl::SslV2:
- case QSsl::SslV3:
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QStringLiteral("unsupported protocol"));
- return;
- case QSsl::AnyProtocol:
- case QSsl::TlsV1SslV3:
- protectionLevel = SocketProtectionLevel_Tls10;
- break;
- case QSsl::TlsV1_0:
- protectionLevel = SocketProtectionLevel_Tls10;
- break;
- case QSsl::TlsV1_1:
- protectionLevel = SocketProtectionLevel_Tls11;
- break;
- case QSsl::TlsV1_2:
- protectionLevel = SocketProtectionLevel_Tls12;
- break;
- case QSsl::TlsV1_0OrLater:
- case QSsl::TlsV1_1OrLater:
- case QSsl::TlsV1_2OrLater:
- case QSsl::TlsV1_3:
- case QSsl::TlsV1_3OrLater:
- // TlsV1_0OrLater, TlsV1_1OrLater and TlsV1_2OrLater are disabled on WinRT
- // because there is no good way to map them to the native API.
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
- QStringLiteral("unsupported protocol"));
- return;
- case QSsl::SecureProtocols:
- // SocketProtectionLevel_Tls12 actually means "use TLS1.0, 1.1 or 1.2"
- // https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.socketprotectionlevel
- protectionLevel = SocketProtectionLevel_Tls12;
- break;
- default:
- protectionLevel = SocketProtectionLevel_Tls12; // default to highest
- protocol = QSsl::TlsV1_2;
- break;
- }
-
- // Sync custom certificates
- const QSet<QSslCertificate> caCertificates = configuration.caCertificates.toSet();
- const QSet<QSslCertificate> newCertificates = caCertificates - previousCaCertificates;
- const QSet<QSslCertificate> oldCertificates = previousCaCertificates - caCertificates;
- g->syncCaCertificates(newCertificates, oldCertificates);
- previousCaCertificates = caCertificates;
-
- continueHandshake();
-}
-
-void QSslSocketBackendPrivate::startServerEncryption()
-{
- Q_UNIMPLEMENTED();
-}
-
-void QSslSocketBackendPrivate::transmit()
-{
- Q_Q(QSslSocket);
-
- if (connectionEncrypted && !writeBuffer.isEmpty()) {
- qint64 totalBytesWritten = 0;
- int nextDataBlockSize;
- while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
- int writtenBytes = plainSocket->write(writeBuffer.readPointer(), nextDataBlockSize);
- writtenBytes = nextDataBlockSize;
-
- writeBuffer.free(writtenBytes);
- totalBytesWritten += writtenBytes;
-
- if (writtenBytes < nextDataBlockSize)
- break;
- }
-
- if (totalBytesWritten > 0) {
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- emittedBytesWritten = true;
- emit q->bytesWritten(totalBytesWritten);
- emittedBytesWritten = false;
- }
- emit q->channelBytesWritten(0, totalBytesWritten);
- }
- }
-
- // Check if we've got any data to be read from the socket.
- int pendingBytes;
- bool bytesRead = false;
- while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
- char *ptr = buffer.reserve(pendingBytes);
- int readBytes = plainSocket->read(ptr, pendingBytes);
- buffer.chop(pendingBytes - readBytes);
- bytesRead = true;
- }
-
- if (bytesRead) {
- if (readyReadEmittedPointer)
- *readyReadEmittedPointer = true;
- emit q->readyRead();
- emit q->channelReadyRead(0);
- }
-
- if (pendingClose) {
- pendingClose = false;
- q->disconnectFromHost();
- }
-}
-
-void QSslSocketBackendPrivate::disconnectFromHost()
-{
- QMetaObject::invokeMethod(connectionHelper.data(), "disconnectSocketFromHost", Qt::QueuedConnection);
-}
-
-void QSslSocketBackendPrivate::disconnected()
-{
-}
-
-QSslCipher QSslSocketBackendPrivate::sessionCipher() const
-{
- return configuration.sessionCipher;
-}
-
-QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
-{
- return configuration.sessionCipher.protocol();
-}
-
-void QSslSocketBackendPrivate::continueHandshake()
-{
- IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor());
- if (qintptr(socket) == -1) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QStringLiteral("At attempt was made to continue the handshake on an invalid socket."));
- return;
- }
-
- HRESULT hr;
- ComPtr<IHostName> hostName;
- const QString host = verificationPeerName.isEmpty() ? plainSocket->peerName()
- : verificationPeerName;
- if (host.isEmpty()) {
- ComPtr<IStreamSocketInformation> info;
- hr = socket->get_Information(&info);
- Q_ASSERT_SUCCEEDED(hr);
- hr = info->get_RemoteAddress(&hostName);
- } else {
- HStringReference hostRef(reinterpret_cast<LPCWSTR>(host.utf16()), host.length());
- hr = g->hostNameFactory->CreateHostName(hostRef.Get(), &hostName);
- Q_ASSERT_SUCCEEDED(hr);
- }
- if (FAILED(hr)) {
- setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError, qt_error_string(hr));
- return;
- }
-
- ComPtr<IStreamSocketControl> control;
- hr = socket->get_Control(&control);
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IStreamSocketControl2> control2;
- hr = control.As(&control2);
- ComPtr<IVector<ChainValidationResult>> ignoreList;
- hr = control2->get_IgnorableServerCertificateErrors(&ignoreList);
- Q_ASSERT_SUCCEEDED(hr);
-
- QSet<QSslError> ignoreErrors = ignoreErrorsList.toSet();
- for (int i = ChainValidationResult_Untrusted; i < ChainValidationResult_OtherErrors + 1; ++i) {
- // Populate the native ignore list - break to add, continue to skip
- switch (i) {
- case ChainValidationResult_Revoked:
- case ChainValidationResult_InvalidSignature:
- case ChainValidationResult_BasicConstraintsError:
- case ChainValidationResult_InvalidCertificateAuthorityPolicy:
- case ChainValidationResult_UnknownCriticalExtension:
- case ChainValidationResult_OtherErrors:
- continue; // The above errors can't be ignored in the handshake
- case ChainValidationResult_Untrusted:
- if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateUntrusted))
- break;
- continue;
- case ChainValidationResult_Expired:
- if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateExpired))
- break;
- continue;
- case ChainValidationResult_IncompleteChain:
- if (ignoreAllSslErrors
- || ignoreErrors.contains(QSslError::InvalidCaCertificate)
- || ignoreErrors.contains(QSslError::UnableToVerifyFirstCertificate)
- || ignoreErrors.contains(QSslError::UnableToGetIssuerCertificate)) {
- break;
- }
- continue;
- case ChainValidationResult_WrongUsage:
- if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::InvalidPurpose))
- break;
- continue;
- case ChainValidationResult_InvalidName:
- if (ignoreAllSslErrors
- || ignoreErrors.contains(QSslError::HostNameMismatch)
- || ignoreErrors.contains(QSslError::SubjectIssuerMismatch)) {
- break;
- }
- continue;
- case ChainValidationResult_RevocationInformationMissing:
- case ChainValidationResult_RevocationFailure:
- default:
- if (ignoreAllSslErrors)
- break;
- continue;
- }
- hr = ignoreList->Append(static_cast<ChainValidationResult>(i));
- Q_ASSERT_SUCCEEDED(hr);
- }
-
- ComPtr<IAsyncAction> op;
- hr = socket->UpgradeToSslAsync(protectionLevel, hostName.Get(), &op);
- if (FAILED(hr)) {
- setErrorAndEmit(QAbstractSocket::SslInternalError,
- QSslSocket::tr("Error creating SSL session: %1").arg(qt_error_string(hr)));
- return;
- }
-
- hr = QEventDispatcherWinRT::runOnXamlThread([this, op]() {
- HRESULT hr = op->put_Completed(Callback<IAsyncActionCompletedHandler>(
- this, &QSslSocketBackendPrivate::onSslUpgrade).Get());
- return hr;
- });
- Q_ASSERT_SUCCEEDED(hr);
-}
-
-HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus)
-{
- Q_Q(QSslSocket);
-
- if (wasDeleted) {
- qCWarning(lcSsl,
- "SSL upgrade callback received after the delegate was deleted. "
- "This may be indicative of an internal bug in the WinRT SSL implementation.");
- return S_OK;
- }
-
- HRESULT hr = action->GetResults();
- QSet<QSslError> errors;
- switch (hr) {
- case SEC_E_INVALID_TOKEN: // Occurs when the server doesn't support the requested protocol
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, qt_error_string(hr));
- q->disconnectFromHost();
- return S_OK;
- default:
- if (FAILED(hr))
- qErrnoWarning(hr, "error"); // Unhandled error; let sslErrors take care of it
- break;
- }
-
- IStreamSocket *socket = reinterpret_cast<IStreamSocket *>(plainSocket->socketDescriptor());
- if (qintptr(socket) == -1) {
- qCWarning(lcSsl,
- "The underlying TCP socket used by the SSL socket is invalid. "
- "This may be indicative of an internal bug in the WinRT SSL implementation.");
- return S_OK;
- }
-
- ComPtr<IStreamSocketInformation> info;
- hr = socket->get_Information(&info);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<IStreamSocketInformation2> info2;
- hr = info.As(&info2);
- Q_ASSERT_SUCCEEDED(hr);
-
- // Cipher
- QSsl::SslProtocol protocol;
- SocketProtectionLevel protectionLevel;
- hr = info->get_ProtectionLevel(&protectionLevel);
- switch (protectionLevel) {
- default:
- protocol = QSsl::UnknownProtocol;
- break;
- case SocketProtectionLevel_Ssl:
- protocol = QSsl::SslV3;
- break;
- case SocketProtectionLevel_Tls10:
- protocol = QSsl::TlsV1_0;
- break;
- case SocketProtectionLevel_Tls11:
- protocol = QSsl::TlsV1_1;
- break;
- case SocketProtectionLevel_Tls12:
- protocol = QSsl::TlsV1_2;
- break;
- }
- configuration.sessionCipher = QSslCipher(QStringLiteral("WINRT"), protocol); // The actual cipher name is not accessible
-
- // Certificate & chain
- ComPtr<ICertificate> certificate;
- hr = info2->get_ServerCertificate(&certificate);
- Q_ASSERT_SUCCEEDED(hr);
-
- QList<QSslCertificate> peerCertificateChain;
- if (certificate) {
- ComPtr<IAsyncOperation<CertificateChain *>> op;
- hr = certificate->BuildChainAsync(nullptr, &op);
- Q_ASSERT_SUCCEEDED(hr);
- ComPtr<ICertificateChain> certificateChain;
- hr = QWinRTFunctions::await(op, certificateChain.GetAddressOf());
- Q_ASSERT_SUCCEEDED(hr);
-
- ComPtr<IVectorView<Certificate *>> certificates;
- hr = certificateChain->GetCertificates(true, &certificates);
- Q_ASSERT_SUCCEEDED(hr);
- quint32 certificatesLength;
- hr = certificates->get_Size(&certificatesLength);
- Q_ASSERT_SUCCEEDED(hr);
- for (quint32 i = 0; i < certificatesLength; ++i) {
- ComPtr<ICertificate> chainCertificate;
- hr = certificates->GetAt(i, &chainCertificate);
- Q_ASSERT_SUCCEEDED(hr);
- peerCertificateChain.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(chainCertificate.Get()));
- }
- }
-
- configuration.peerCertificate = certificate ? QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get())
- : QSslCertificate();
- configuration.peerCertificateChain = peerCertificateChain;
-
- // Errors
- ComPtr<IVectorView<ChainValidationResult>> chainValidationResults;
- hr = info2->get_ServerCertificateErrors(&chainValidationResults);
- Q_ASSERT_SUCCEEDED(hr);
- quint32 size;
- hr = chainValidationResults->get_Size(&size);
- Q_ASSERT_SUCCEEDED(hr);
- for (quint32 i = 0; i < size; ++i) {
- ChainValidationResult result;
- hr = chainValidationResults->GetAt(i, &result);
- Q_ASSERT_SUCCEEDED(hr);
- switch (result) {
- case ChainValidationResult_Success:
- break;
- case ChainValidationResult_Untrusted:
- errors.insert(QSslError::CertificateUntrusted);
- break;
- case ChainValidationResult_Revoked:
- errors.insert(QSslError::CertificateRevoked);
- break;
- case ChainValidationResult_Expired:
- errors.insert(QSslError::CertificateExpired);
- break;
- case ChainValidationResult_IncompleteChain:
- errors.insert(QSslError::UnableToGetIssuerCertificate);
- break;
- case ChainValidationResult_InvalidSignature:
- errors.insert(QSslError::CertificateSignatureFailed);
- break;
- case ChainValidationResult_WrongUsage:
- errors.insert(QSslError::InvalidPurpose);
- break;
- case ChainValidationResult_InvalidName:
- errors.insert(QSslError::HostNameMismatch);
- break;
- case ChainValidationResult_InvalidCertificateAuthorityPolicy:
- errors.insert(QSslError::InvalidCaCertificate);
- break;
- default:
- errors.insert(QSslError::UnspecifiedError);
- break;
- }
- }
-
- sslErrors = errors.toList();
-
- // Peer validation
- if (!configuration.peerCertificate.isNull()) {
- const QString peerName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
- if (!isMatchingHostname(configuration.peerCertificate, peerName)) {
- // No matches in common names or alternate names.
- const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
- const int index = sslErrors.indexOf(QSslError::HostNameMismatch);
- if (index >= 0) // Replace the existing error
- sslErrors[index] = error;
- else
- sslErrors.append(error);
- emit q->peerVerifyError(error);
- }
-
- // Peer validation required, but no certificate is present
- } else if (configuration.peerVerifyMode == QSslSocket::VerifyPeer
- || configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer) {
- QSslError error(QSslError::NoPeerCertificate);
- sslErrors.append(error);
- emit q->peerVerifyError(error);
- }
-
- // Peer chain validation
- for (const QSslCertificate &certificate : qAsConst(peerCertificateChain)) {
- if (!QSslCertificatePrivate::isBlacklisted(certificate))
- continue;
-
- QSslError error(QSslError::CertificateBlacklisted, certificate);
- sslErrors.append(error);
- emit q->peerVerifyError(error);
- }
-
- if (!sslErrors.isEmpty()) {
- emit q->sslErrors(sslErrors);
- setErrorAndEmit(QAbstractSocket::SslHandshakeFailedError, sslErrors.constFirst().errorString());
-
- // Disconnect if there are any non-ignorable errors
- for (const QSslError &error : qAsConst(sslErrors)) {
- if (ignoreErrorsList.contains(error))
- continue;
- q->disconnectFromHost();
- return S_OK;
- }
- }
-
- if (readBufferMaxSize)
- plainSocket->setReadBufferSize(readBufferMaxSize);
-
- connectionEncrypted = true;
- emit q->encrypted();
-
- // The write buffer may already have data written to it, so we need to call transmit.
- // This has to be done in 'q's thread, and not in the current thread (the XAML thread).
- QMetaObject::invokeMethod(q, [this](){ transmit(); });
-
- if (pendingClose) {
- pendingClose = false;
- q->disconnectFromHost();
- }
-
- return S_OK;
-}
-
-QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
-{
- Q_UNIMPLEMENTED();
- Q_UNUSED(certificateChain)
- Q_UNUSED(hostName)
- QList<QSslError> errors;
-
- return errors;
-}
-
-bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase)
-{
- Q_UNIMPLEMENTED();
- Q_UNUSED(device)
- Q_UNUSED(key)
- Q_UNUSED(cert)
- Q_UNUSED(caCertificates)
- Q_UNUSED(passPhrase)
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_winrt_p.h b/src/network/ssl/qsslsocket_winrt_p.h
deleted file mode 100644
index 030db6d4fa..0000000000
--- a/src/network/ssl/qsslsocket_winrt_p.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSSLSOCKET_WINRT_P_H
-#define QSSLSOCKET_WINRT_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QtNetwork library. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "qsslsocket_p.h"
-
-#include <wrl.h>
-#include <windows.networking.sockets.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSslSocketConnectionHelper : public QObject
-{
- Q_OBJECT
-public:
- QSslSocketConnectionHelper(QSslSocketBackendPrivate *d)
- : d(d) { }
-
- Q_INVOKABLE void disconnectSocketFromHost();
-
-private:
- QSslSocketBackendPrivate *d;
-};
-
-class QSslSocketBackendPrivate : public QSslSocketPrivate
-{
- Q_DECLARE_PUBLIC(QSslSocket)
-public:
- QSslSocketBackendPrivate();
- ~QSslSocketBackendPrivate();
-
- // Platform specific functions
- void startClientEncryption() override;
- void startServerEncryption() override;
- void transmit() override;
- void disconnectFromHost() override;
- void disconnected() override;
- QSslCipher sessionCipher() const override;
- QSsl::SslProtocol sessionProtocol() const override;
- void continueHandshake() override;
-
- static QList<QSslCipher> defaultCiphers();
- static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
- static bool importPkcs12(QIODevice *device,
- QSslKey *key, QSslCertificate *cert,
- QList<QSslCertificate> *caCertificates,
- const QByteArray &passPhrase);
-
-private:
- HRESULT onSslUpgrade(ABI::Windows::Foundation::IAsyncAction *,
- ABI::Windows::Foundation::AsyncStatus);
-
- QScopedPointer<QSslSocketConnectionHelper> connectionHelper;
- ABI::Windows::Networking::Sockets::SocketProtectionLevel protectionLevel;
- QSet<QSslCertificate> previousCaCertificates;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSSLSOCKET_WINRT_P_H
diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp
new file mode 100644
index 0000000000..761ab33fbe
--- /dev/null
+++ b/src/network/ssl/qtlsbackend.cpp
@@ -0,0 +1,2357 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtlsbackend_p.h"
+
+#if QT_CONFIG(ssl)
+#include "qsslpresharedkeyauthenticator_p.h"
+#include "qsslpresharedkeyauthenticator.h"
+#include "qsslsocket_p.h"
+#include "qsslcipher_p.h"
+#include "qsslkey_p.h"
+#include "qsslkey.h"
+#endif
+
+#include "qssl_p.h"
+
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include "QtCore/qapplicationstatic.h"
+#include <QtCore/qbytearray.h>
+#include <QtCore/qmutex.h>
+
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_APPLICATION_STATIC(QFactoryLoader, qtlsbLoader, QTlsBackend_iid,
+ QStringLiteral("/tls"))
+
+namespace {
+
+class BackendCollection
+{
+public:
+ void addBackend(QTlsBackend *backend)
+ {
+ Q_ASSERT(backend);
+ Q_ASSERT(std::find(backends.begin(), backends.end(), backend) == backends.end());
+ const QMutexLocker locker(&collectionMutex);
+ backends.push_back(backend);
+ }
+
+ void removeBackend(QTlsBackend *backend)
+ {
+ Q_ASSERT(backend);
+ const QMutexLocker locker(&collectionMutex);
+ const auto it = std::find(backends.begin(), backends.end(), backend);
+ Q_ASSERT(it != backends.end());
+ backends.erase(it);
+ }
+
+ bool tryPopulateCollection()
+ {
+ if (!qtlsbLoader())
+ return false;
+
+ Q_CONSTINIT static QBasicMutex mutex;
+ const QMutexLocker locker(&mutex);
+ if (backends.size())
+ return true;
+
+#if QT_CONFIG(library)
+ qtlsbLoader->update();
+#endif
+ int index = 0;
+ while (qtlsbLoader->instance(index))
+ ++index;
+
+ return true;
+ }
+
+ QList<QString> backendNames()
+ {
+ QList<QString> names;
+ if (!tryPopulateCollection())
+ return names;
+
+ const QMutexLocker locker(&collectionMutex);
+ if (!backends.size())
+ return names;
+
+ names.reserve(backends.size());
+ for (const auto *backend : backends) {
+ if (backend->isValid())
+ names.append(backend->backendName());
+ }
+
+ return names;
+ }
+
+ QTlsBackend *backend(const QString &name)
+ {
+ if (!tryPopulateCollection())
+ return nullptr;
+
+ const QMutexLocker locker(&collectionMutex);
+ const auto it = std::find_if(backends.begin(), backends.end(),
+ [&name](const auto *fct) {return fct->backendName() == name;});
+
+ return it == backends.end() ? nullptr : *it;
+ }
+
+private:
+ std::vector<QTlsBackend *> backends;
+ QMutex collectionMutex;
+};
+
+} // Unnamed namespace
+
+Q_GLOBAL_STATIC(BackendCollection, backends);
+
+/*!
+ \class QTlsBackend
+ \internal (Network-private)
+ \brief QTlsBackend is a factory class, providing implementations
+ for the QSsl classes.
+
+ The purpose of QTlsBackend is to enable and simplify the addition
+ of new TLS backends to be used by QSslSocket and related classes.
+ Starting from Qt 6.1, these backends have plugin-based design (and
+ thus can co-exist simultaneously, unlike pre 6.1 times), although
+ any given run of a program can only use one of them.
+
+ Inheriting from QTlsBackend and creating an object of such
+ a class adds a new backend into the list of available TLS backends.
+
+ A new backend must provide a list of classes, features and protocols
+ it supports, and override the corresponding virtual functions that
+ create backend-specific implementations for these QSsl-classes.
+
+ The base abstract class - QTlsBackend - provides, where possible,
+ default implementations of its virtual member functions. These
+ default implementations can be overridden by a derived backend
+ class, if needed.
+
+ QTlsBackend also provides some auxiliary functions that a derived
+ backend class can use to interact with the internals of network-private classes.
+
+ \sa QSslSocket::availableBackends(), supportedFeatures(), supportedProtocols(), implementedClasses()
+*/
+
+/*!
+ \fn QString QTlsBackend::backendName() const
+ \internal
+ Returns the name of this backend. The name will be reported by QSslSocket::availableBackends().
+ Example of backend names: "openssl", "schannel", "securetransport".
+
+ \sa QSslSocket::availableBackends(), isValid()
+*/
+
+const QString QTlsBackend::builtinBackendNames[] = {
+ QStringLiteral("schannel"),
+ QStringLiteral("securetransport"),
+ QStringLiteral("openssl"),
+ QStringLiteral("cert-only")
+};
+
+/*!
+ \internal
+ The default constructor, adds a new backend to the list of available backends.
+
+ \sa ~QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends()
+*/
+QTlsBackend::QTlsBackend()
+{
+ if (backends())
+ backends->addBackend(this);
+
+ if (QCoreApplication::instance()) {
+ connect(QCoreApplication::instance(), &QCoreApplication::destroyed, this, [this] {
+ delete this;
+ });
+ }
+}
+
+/*!
+ \internal
+ Removes this backend from the list of available backends.
+
+ \sa QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends()
+*/
+QTlsBackend::~QTlsBackend()
+{
+ if (backends())
+ backends->removeBackend(this);
+}
+
+/*!
+ \internal
+ Returns \c true if this backend was initialised successfully. The default implementation
+ always returns \c true.
+
+ \note This function must be overridden if a particular backend has a non-trivial initialization
+ that can fail. If reimplemented, returning \c false will exclude this backend from the list of
+ backends, reported as available by QSslSocket.
+
+ \sa QSslSocket::availableBackends()
+*/
+
+bool QTlsBackend::isValid() const
+{
+ return true;
+}
+
+/*!
+ \internal
+ Returns an implementations-specific integer value, representing the TLS library's
+ version, that is currently used by this backend (i.e. runtime library version).
+ The default implementation returns 0.
+
+ \sa tlsLibraryBuildVersionNumber()
+*/
+long QTlsBackend::tlsLibraryVersionNumber() const
+{
+ return 0;
+}
+
+/*!
+ \internal
+ Returns an implementation-specific string, representing the TLS library's version,
+ that is currently used by this backend (i.e. runtime library version). The default
+ implementation returns an empty string.
+
+ \sa tlsLibraryBuildVersionString()
+*/
+
+QString QTlsBackend::tlsLibraryVersionString() const
+{
+ return {};
+}
+
+/*!
+ \internal
+ Returns an implementation-specific integer value, representing the TLS library's
+ version that this backend was built against (i.e. compile-time library version).
+ The default implementation returns 0.
+
+ \sa tlsLibraryVersionNumber()
+*/
+
+long QTlsBackend::tlsLibraryBuildVersionNumber() const
+{
+ return 0;
+}
+
+/*!
+ \internal
+ Returns an implementation-specific string, representing the TLS library's version
+ that this backend was built against (i.e. compile-time version). The default
+ implementation returns an empty string.
+
+ \sa tlsLibraryVersionString()
+*/
+QString QTlsBackend::tlsLibraryBuildVersionString() const
+{
+ return {};
+}
+
+/*!
+ \internal
+ QSslSocket and related classes call this function to ensure that backend's internal
+ resources - e.g. CA certificates, or ciphersuites - were properly initialized.
+*/
+void QTlsBackend::ensureInitialized() const
+{
+}
+
+#define REPORT_MISSING_SUPPORT(message) \
+ qCWarning(lcSsl) << "The backend" << backendName() << message
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::Key is present in this backend's implementedClasses(),
+ the backend must reimplement this method to return a dynamically-allocated instance
+ of an implementation-specific type, inheriting from the class QTlsPrivate::TlsKey.
+ The default implementation of this function returns \nullptr.
+
+ \sa QSslKey, implementedClasses(), QTlsPrivate::TlsKey
+*/
+QTlsPrivate::TlsKey *QTlsBackend::createKey() const
+{
+ REPORT_MISSING_SUPPORT("does not support QSslKey");
+ return nullptr;
+}
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::Certificate is present in this backend's implementedClasses(),
+ the backend must reimplement this method to return a dynamically-allocated instance of an
+ implementation-specific type, inheriting from the class QTlsPrivate::X509Certificate.
+ The default implementation of this function returns \nullptr.
+
+ \sa QSslCertificate, QTlsPrivate::X509Certificate, implementedClasses()
+*/
+QTlsPrivate::X509Certificate *QTlsBackend::createCertificate() const
+{
+ REPORT_MISSING_SUPPORT("does not support QSslCertificate");
+ return nullptr;
+}
+
+/*!
+ \internal
+ This function returns a list of system CA certificates - e.g. certificates, loaded
+ from a system store, if available. This function allows implementation of the class
+ QSslConfiguration. The default implementation of this function returns an empty list.
+
+ \sa QSslCertificate, QSslConfiguration
+*/
+QList<QSslCertificate> QTlsBackend::systemCaCertificates() const
+{
+ REPORT_MISSING_SUPPORT("does not provide system CA certificates");
+ return {};
+}
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::Socket is present in this backend's implementedClasses(),
+ the backend must reimplement this method to return a dynamically-allocated instance of an
+ implementation-specific type, inheriting from the class QTlsPrivate::TlsCryptograph.
+ The default implementation of this function returns \nullptr.
+
+ \sa QSslSocket, QTlsPrivate::TlsCryptograph, implementedClasses()
+*/
+QTlsPrivate::TlsCryptograph *QTlsBackend::createTlsCryptograph() const
+{
+ REPORT_MISSING_SUPPORT("does not support QSslSocket");
+ return nullptr;
+}
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::Dtls is present in this backend's implementedClasses(),
+ the backend must reimplement this method to return a dynamically-allocated instance of an
+ implementation-specific type, inheriting from the class QTlsPrivate::DtlsCryptograph.
+ The default implementation of this function returns \nullptr.
+
+ \sa QDtls, QTlsPrivate::DtlsCryptograph, implementedClasses()
+*/
+QTlsPrivate::DtlsCryptograph *QTlsBackend::createDtlsCryptograph(QDtls *qObject, int mode) const
+{
+ Q_UNUSED(qObject);
+ Q_UNUSED(mode);
+ REPORT_MISSING_SUPPORT("does not support QDtls");
+ return nullptr;
+}
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::DtlsCookie is present in this backend's implementedClasses(),
+ the backend must reimplement this method to return a dynamically-allocated instance of an
+ implementation-specific type, inheriting from the class QTlsPrivate::DtlsCookieVerifier. The
+ default implementation returns \nullptr.
+
+ \sa QDtlsClientVerifier, QTlsPrivate::DtlsCookieVerifier, implementedClasses()
+*/
+QTlsPrivate::DtlsCookieVerifier *QTlsBackend::createDtlsCookieVerifier() const
+{
+ REPORT_MISSING_SUPPORT("does not support DTLS cookies");
+ return nullptr;
+}
+
+/*!
+ \internal
+ If QSsl::SupportedFeature::CertificateVerification is present in this backend's
+ supportedFeatures(), the backend must reimplement this method to return a pointer
+ to a function, that checks a certificate (or a chain of certificates) against available
+ CA certificates. The default implementation returns \nullptr.
+
+ \sa supportedFeatures(), QSslCertificate
+*/
+
+QTlsPrivate::X509ChainVerifyPtr QTlsBackend::X509Verifier() const
+{
+ REPORT_MISSING_SUPPORT("does not support (manual) certificate verification");
+ return nullptr;
+}
+
+/*!
+ \internal
+ Returns a pointer to function, that reads certificates in PEM format. The
+ default implementation returns \nullptr.
+
+ \sa QSslCertificate
+*/
+QTlsPrivate::X509PemReaderPtr QTlsBackend::X509PemReader() const
+{
+ REPORT_MISSING_SUPPORT("cannot read PEM format");
+ return nullptr;
+}
+
+/*!
+ \internal
+ Returns a pointer to function, that can read certificates in DER format.
+ The default implementation returns \nullptr.
+
+ \sa QSslCertificate
+*/
+QTlsPrivate::X509DerReaderPtr QTlsBackend::X509DerReader() const
+{
+ REPORT_MISSING_SUPPORT("cannot read DER format");
+ return nullptr;
+}
+
+/*!
+ \internal
+ Returns a pointer to function, that can read certificates in PKCS 12 format.
+ The default implementation returns \nullptr.
+
+ \sa QSslCertificate
+*/
+QTlsPrivate::X509Pkcs12ReaderPtr QTlsBackend::X509Pkcs12Reader() const
+{
+ REPORT_MISSING_SUPPORT("cannot read PKCS12 format");
+ return nullptr;
+}
+
+/*!
+ \internal
+ If QSsl::ImplementedClass::EllipticCurve is present in this backend's implementedClasses(),
+ and the backend provides information about supported curves, it must reimplement this
+ method to return a list of unique identifiers of the supported elliptic curves. The default
+ implementation returns an empty list.
+
+ \note The meaning of a curve identifier is implementation-specific.
+
+ \sa implemenedClasses(), QSslEllipticCurve
+*/
+QList<int> QTlsBackend::ellipticCurvesIds() const
+{
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return {};
+}
+
+/*!
+ \internal
+ If this backend provides information about available elliptic curves, this
+ function should return a unique integer identifier for a curve named \a name,
+ which is a conventional short name for the curve. The default implementation
+ returns 0.
+
+ \note The meaning of a curve identifier is implementation-specific.
+
+ \sa QSslEllipticCurve::shortName()
+*/
+int QTlsBackend::curveIdFromShortName(const QString &name) const
+{
+ Q_UNUSED(name);
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return 0;
+}
+
+/*!
+ \internal
+ If this backend provides information about available elliptic curves, this
+ function should return a unique integer identifier for a curve named \a name,
+ which is a conventional long name for the curve. The default implementation
+ returns 0.
+
+ \note The meaning of a curve identifier is implementation-specific.
+
+ \sa QSslElliptiCurve::longName()
+*/
+int QTlsBackend::curveIdFromLongName(const QString &name) const
+{
+ Q_UNUSED(name);
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return 0;
+}
+
+/*!
+ \internal
+ If this backend provides information about available elliptic curves,
+ this function should return a conventional short name for a curve identified
+ by \a cid. The default implementation returns an empty string.
+
+ \note The meaning of a curve identifier is implementation-specific.
+
+ \sa ellipticCurvesIds(), QSslEllipticCurve::shortName()
+*/
+QString QTlsBackend::shortNameForId(int cid) const
+{
+ Q_UNUSED(cid);
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return {};
+}
+
+/*!
+ \internal
+ If this backend provides information about available elliptic curves,
+ this function should return a conventional long name for a curve identified
+ by \a cid. The default implementation returns an empty string.
+
+ \note The meaning of a curve identifier is implementation-specific.
+
+ \sa ellipticCurvesIds(), QSslEllipticCurve::shortName()
+*/
+QString QTlsBackend::longNameForId(int cid) const
+{
+ Q_UNUSED(cid);
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return {};
+}
+
+/*!
+ \internal
+ Returns true if the elliptic curve identified by \a cid is one of the named
+ curves, that can be used in the key exchange when using an elliptic curve
+ cipher with TLS; false otherwise. The default implementation returns false.
+
+ \note The meaning of curve identifier is implementation-specific.
+*/
+bool QTlsBackend::isTlsNamedCurve(int cid) const
+{
+ Q_UNUSED(cid);
+ REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
+ return false;
+}
+
+/*!
+ \internal
+ If this backend supports the class QSslDiffieHellmanParameters, this function is
+ needed for construction of a QSslDiffieHellmanParameters from DER encoded data.
+ This function is expected to return a value that matches an enumerator in
+ QSslDiffieHellmanParameters::Error enumeration. The default implementation of this
+ function returns 0 (equals to QSslDiffieHellmanParameters::NoError).
+
+ \sa QSslDiffieHellmanParameters, implementedClasses()
+*/
+int QTlsBackend::dhParametersFromDer(const QByteArray &derData, QByteArray *data) const
+{
+ Q_UNUSED(derData);
+ Q_UNUSED(data);
+ REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in DER format");
+ return {};
+}
+
+/*!
+ \internal
+ If this backend supports the class QSslDiffieHellmanParameters, this function is
+ is needed for construction of a QSslDiffieHellmanParameters from PEM encoded data.
+ This function is expected to return a value that matches an enumerator in
+ QSslDiffieHellmanParameters::Error enumeration. The default implementation of this
+ function returns 0 (equals to QSslDiffieHellmanParameters::NoError).
+
+ \sa QSslDiffieHellmanParameters, implementedClasses()
+*/
+int QTlsBackend::dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const
+{
+ Q_UNUSED(pemData);
+ Q_UNUSED(data);
+ REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in PEM format");
+ return {};
+}
+
+/*!
+ \internal
+ Returns a list of names of available backends.
+
+ \note This list contains only properly initialized backends.
+
+ \sa QTlsBackend(), isValid()
+*/
+QList<QString> QTlsBackend::availableBackendNames()
+{
+ if (!backends())
+ return {};
+
+ return backends->backendNames();
+}
+
+/*!
+ \internal
+ Returns the name of the backend that QSslSocket() would use by default. If no
+ backend was found, the function returns an empty string.
+*/
+QString QTlsBackend::defaultBackendName()
+{
+ // We prefer OpenSSL as default:
+ const auto names = availableBackendNames();
+ auto name = builtinBackendNames[nameIndexOpenSSL];
+ if (names.contains(name))
+ return name;
+ name = builtinBackendNames[nameIndexSchannel];
+ if (names.contains(name))
+ return name;
+ name = builtinBackendNames[nameIndexSecureTransport];
+ if (names.contains(name))
+ return name;
+
+ const auto pos = std::find_if(names.begin(), names.end(), [](const auto &name) {
+ return name != builtinBackendNames[nameIndexCertOnly];
+ });
+
+ if (pos != names.end())
+ return *pos;
+
+ if (names.size())
+ return names[0];
+
+ return {};
+}
+
+/*!
+ \internal
+ Returns a backend named \a backendName, if it exists.
+ Otherwise, it returns \nullptr.
+
+ \sa backendName(), QSslSocket::availableBackends()
+*/
+QTlsBackend *QTlsBackend::findBackend(const QString &backendName)
+{
+ if (!backends())
+ return {};
+
+ if (auto *fct = backends->backend(backendName))
+ return fct;
+
+ qCWarning(lcSsl) << "Cannot create unknown backend named" << backendName;
+ return nullptr;
+}
+
+/*!
+ \internal
+ Returns the backend that QSslSocket is using. If Qt was built without TLS support,
+ this function returns a minimal backend that only supports QSslCertificate.
+
+ \sa defaultBackend()
+*/
+QTlsBackend *QTlsBackend::activeOrAnyBackend()
+{
+#if QT_CONFIG(ssl)
+ return QSslSocketPrivate::tlsBackendInUse();
+#else
+ return findBackend(defaultBackendName());
+#endif // QT_CONFIG(ssl)
+}
+
+/*!
+ \internal
+ Returns a list of TLS and DTLS protocol versions, that a backend named
+ \a backendName supports.
+
+ \note This list is supposed to also include range-based versions, which
+ allows negotiation of protocols during the handshake, so that these versions
+ can be used when configuring QSslSocket (e.g. QSsl::TlsV1_2OrLater).
+
+ \sa QSsl::SslProtocol
+*/
+QList<QSsl::SslProtocol> QTlsBackend::supportedProtocols(const QString &backendName)
+{
+ if (!backends())
+ return {};
+
+ if (const auto *fct = backends->backend(backendName))
+ return fct->supportedProtocols();
+
+ return {};
+}
+
+/*!
+ \internal
+ Returns a list of features that a backend named \a backendName supports. E.g.
+ a backend may support PSK (pre-shared keys, defined as QSsl::SupportedFeature::Psk)
+ or ALPN (application layer protocol negotiation, identified by
+ QSsl::SupportedFeature::ClientSideAlpn or QSsl::SupportedFeature::ServerSideAlpn).
+
+ \sa QSsl::SupportedFeature
+*/
+QList<QSsl::SupportedFeature> QTlsBackend::supportedFeatures(const QString &backendName)
+{
+ if (!backends())
+ return {};
+
+ if (const auto *fct = backends->backend(backendName))
+ return fct->supportedFeatures();
+
+ return {};
+}
+
+/*!
+ \internal
+ Returns a list of classes that a backend named \a backendName supports. E.g. a backend
+ may implement QSslSocket (QSsl::ImplementedClass::Socket), and QDtls
+ (QSsl::ImplementedClass::Dtls).
+
+ \sa QSsl::ImplementedClass
+*/
+QList<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &backendName)
+{
+ if (!backends())
+ return {};
+
+ if (const auto *fct = backends->backend(backendName))
+ return fct->implementedClasses();
+
+ return {};
+}
+
+/*!
+ \internal
+ Auxiliary function. Initializes \a key to use \a keyBackend.
+*/
+void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend)
+{
+#if QT_CONFIG(ssl)
+ key.d->backend.reset(keyBackend);
+#else
+ Q_UNUSED(key);
+ Q_UNUSED(keyBackend);
+#endif // QT_CONFIG(ssl)
+}
+
+/*!
+ \internal
+ Auxiliary function. Initializes client-side \a auth using the \a hint, \a hintLength,
+ \a maxIdentityLength and \a maxPskLen.
+*/
+void QTlsBackend::setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint,
+ int hintLength, unsigned maxIdentityLen, unsigned maxPskLen)
+{
+ Q_ASSERT(auth);
+#if QT_CONFIG(ssl)
+ if (hint)
+ auth->d->identityHint = QByteArray::fromRawData(hint, hintLength); // it's NUL terminated, but do not include the NUL
+
+ auth->d->maximumIdentityLength = int(maxIdentityLen) - 1; // needs to be NUL terminated
+ auth->d->maximumPreSharedKeyLength = int(maxPskLen);
+#else
+ Q_UNUSED(auth);
+ Q_UNUSED(hint);
+ Q_UNUSED(hintLength);
+ Q_UNUSED(maxIdentityLen);
+ Q_UNUSED(maxPskLen);
+#endif
+}
+
+/*!
+ \internal
+ Auxiliary function. Initializes server-side \a auth using the \a identity, \a identityHint and
+ \a maxPskLen.
+*/
+void QTlsBackend::setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity,
+ const QByteArray &identityHint, unsigned int maxPskLen)
+{
+#if QT_CONFIG(ssl)
+ Q_ASSERT(auth);
+ auth->d->identityHint = identityHint;
+ auth->d->identity = identity;
+ auth->d->maximumIdentityLength = 0; // user cannot set an identity
+ auth->d->maximumPreSharedKeyLength = int(maxPskLen);
+#else
+ Q_UNUSED(auth);
+ Q_UNUSED(identity);
+ Q_UNUSED(identityHint);
+ Q_UNUSED(maxPskLen);
+#endif
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ \internal
+ Auxiliary function. Creates a new QSslCipher from \a descriptionOneLine, \a bits
+ and \a supportedBits. \a descriptionOneLine consists of several fields, separated by
+ whitespace. These include: cipher name, protocol version, key exchange method,
+ authentication method, encryption method, message digest (Mac). Example:
+ "ECDHE-RSA-AES256-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD"
+*/
+QSslCipher QTlsBackend::createCiphersuite(const QString &descriptionOneLine, int bits, int supportedBits)
+{
+ QSslCipher ciph;
+
+ const auto descriptionList = QStringView{descriptionOneLine}.split(u' ', Qt::SkipEmptyParts);
+ if (descriptionList.size() > 5) {
+ ciph.d->isNull = false;
+ ciph.d->name = descriptionList.at(0).toString();
+
+ QStringView protoString = descriptionList.at(1);
+ ciph.d->protocolString = protoString.toString();
+ ciph.d->protocol = QSsl::UnknownProtocol;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ if (protoString.startsWith(u"TLSv1")) {
+ QStringView tail = protoString.sliced(5);
+ if (tail.startsWith(u'.')) {
+ tail = tail.sliced(1);
+ if (tail == u"3")
+ ciph.d->protocol = QSsl::TlsV1_3;
+ else if (tail == u"2")
+ ciph.d->protocol = QSsl::TlsV1_2;
+ else if (tail == u"1")
+ ciph.d->protocol = QSsl::TlsV1_1;
+ } else if (tail.isEmpty()) {
+ ciph.d->protocol = QSsl::TlsV1_0;
+ }
+ }
+QT_WARNING_POP
+
+ if (descriptionList.at(2).startsWith("Kx="_L1))
+ ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString();
+ if (descriptionList.at(3).startsWith("Au="_L1))
+ ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString();
+ if (descriptionList.at(4).startsWith("Enc="_L1))
+ ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString();
+ ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == "export"_L1);
+
+ ciph.d->bits = bits;
+ ciph.d->supportedBits = supportedBits;
+ }
+
+ return ciph;
+}
+
+/*!
+ \internal
+ Auxiliary function. Creates a new QSslCipher from \a suiteName, \a protocol version and
+ \a protocolString. For example:
+ \code
+ createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, QSsl::TlsV1_2, "TLSv1.2"_L1);
+ \endcode
+*/
+QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
+ const QString &protocolString)
+{
+ QSslCipher ciph;
+
+ if (!suiteName.size())
+ return ciph;
+
+ ciph.d->isNull = false;
+ ciph.d->name = suiteName;
+ ciph.d->protocol = protocol;
+ ciph.d->protocolString = protocolString;
+
+ const auto bits = QStringView{ciph.d->name}.split(u'-');
+ if (bits.size() >= 2) {
+ if (bits.size() == 2 || bits.size() == 3)
+ ciph.d->keyExchangeMethod = "RSA"_L1;
+ else if (bits.front() == "DH"_L1 || bits.front() == "DHE"_L1)
+ ciph.d->keyExchangeMethod = "DH"_L1;
+ else if (bits.front() == "ECDH"_L1 || bits.front() == "ECDHE"_L1)
+ ciph.d->keyExchangeMethod = "ECDH"_L1;
+ else
+ qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name;
+
+ if (bits.size() == 2 || bits.size() == 3)
+ ciph.d->authenticationMethod = "RSA"_L1;
+ else if (ciph.d->name.contains("-ECDSA-"_L1))
+ ciph.d->authenticationMethod = "ECDSA"_L1;
+ else if (ciph.d->name.contains("-RSA-"_L1))
+ ciph.d->authenticationMethod = "RSA"_L1;
+ else
+ qCWarning(lcSsl) << "Unknown Au" << ciph.d->name;
+
+ if (ciph.d->name.contains("RC4-"_L1)) {
+ ciph.d->encryptionMethod = "RC4(128)"_L1;
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("DES-CBC3-"_L1)) {
+ ciph.d->encryptionMethod = "3DES(168)"_L1;
+ ciph.d->bits = 168;
+ ciph.d->supportedBits = 168;
+ } else if (ciph.d->name.contains("AES128-"_L1)) {
+ ciph.d->encryptionMethod = "AES(128)"_L1;
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("AES256-GCM"_L1)) {
+ ciph.d->encryptionMethod = "AESGCM(256)"_L1;
+ ciph.d->bits = 256;
+ ciph.d->supportedBits = 256;
+ } else if (ciph.d->name.contains("AES256-"_L1)) {
+ ciph.d->encryptionMethod = "AES(256)"_L1;
+ ciph.d->bits = 256;
+ ciph.d->supportedBits = 256;
+ } else if (ciph.d->name.contains("CHACHA20-"_L1)) {
+ ciph.d->encryptionMethod = "CHACHA20"_L1;
+ ciph.d->bits = 256;
+ ciph.d->supportedBits = 256;
+ } else if (ciph.d->name.contains("NULL-"_L1)) {
+ ciph.d->encryptionMethod = "NULL"_L1;
+ } else {
+ qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name;
+ }
+ }
+ return ciph;
+}
+
+/*!
+ \internal
+ Auxiliary function. Creates a new QSslCipher from \a name, \a keyExchangeMethod, \a encryptionMethod,
+ \a authenticationMethod, \a bits, \a protocol version and \a protocolString.
+ For example:
+ \code
+ createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, "ECDH"_L1, "AES"_L1, "RSA"_L1, 256,
+ QSsl::TlsV1_2, "TLSv1.2"_L1);
+ \endcode
+*/
+QSslCipher QTlsBackend::createCiphersuite(const QString &name, const QString &keyExchangeMethod,
+ const QString &encryptionMethod,
+ const QString &authenticationMethod,
+ int bits, QSsl::SslProtocol protocol,
+ const QString &protocolString)
+{
+ QSslCipher cipher;
+ cipher.d->isNull = false;
+ cipher.d->name = name;
+ cipher.d->bits = bits;
+ cipher.d->supportedBits = bits;
+ cipher.d->keyExchangeMethod = keyExchangeMethod;
+ cipher.d->encryptionMethod = encryptionMethod;
+ cipher.d->authenticationMethod = authenticationMethod;
+ cipher.d->protocol = protocol;
+ cipher.d->protocolString = protocolString;
+ return cipher;
+}
+
+/*!
+ \internal
+ Returns an implementation-specific list of ciphersuites that can be used by QSslSocket.
+
+ \sa QSslConfiguration::defaultCiphers()
+*/
+QList<QSslCipher> QTlsBackend::defaultCiphers()
+{
+ return QSslSocketPrivate::defaultCiphers();
+}
+
+/*!
+ \internal
+ Returns an implementation-specific list of ciphersuites that can be used by QDtls.
+*/
+QList<QSslCipher> QTlsBackend::defaultDtlsCiphers()
+{
+ return QSslSocketPrivate::defaultDtlsCiphers();
+}
+
+/*!
+ \internal
+ Sets \a ciphers as defaults ciphers that QSslSocket can use.
+
+ \sa defaultCiphers()
+*/
+void QTlsBackend::setDefaultCiphers(const QList<QSslCipher> &ciphers)
+{
+ QSslSocketPrivate::setDefaultCiphers(ciphers);
+}
+
+/*!
+ \internal
+ Sets \a ciphers as defaults ciphers that QDtls can use.
+
+ \sa defaultDtlsCiphers()
+*/
+void QTlsBackend::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
+{
+ QSslSocketPrivate::setDefaultDtlsCiphers(ciphers);
+}
+
+/*!
+ \internal
+ Sets \a ciphers as a list of supported ciphers.
+
+ \sa QSslConfiguration::supportedCiphers()
+*/
+void QTlsBackend::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers)
+{
+ QSslSocketPrivate::setDefaultSupportedCiphers(ciphers);
+}
+
+/*!
+ \internal
+ Sets the list of QSslEllipticCurve objects, that QSslConfiguration::supportedEllipticCurves()
+ returns, to ones that are supported by this backend.
+*/
+void QTlsBackend::resetDefaultEllipticCurves()
+{
+ QSslSocketPrivate::resetDefaultEllipticCurves();
+}
+
+/*!
+ Sets \a certs as a list of certificates, that QSslConfiguration::caCertificates()
+ reports.
+
+ \sa QSslConfiguration::defaultConfiguration(), QSslConfiguration::caCertificates()
+*/
+void QTlsBackend::setDefaultCaCertificates(const QList<QSslCertificate> &certs)
+{
+ QSslSocketPrivate::setDefaultCaCertificates(certs);
+}
+
+/*!
+ \internal
+ Returns true if \a configuration allows loading root certificates on demand.
+*/
+bool QTlsBackend::rootLoadingOnDemandAllowed(const QSslConfiguration &configuration)
+{
+ return configuration.d->allowRootCertOnDemandLoading;
+}
+
+/*!
+ \internal
+ Stores \a peerCert in the \a configuration.
+*/
+void QTlsBackend::storePeerCertificate(QSslConfiguration &configuration,
+ const QSslCertificate &peerCert)
+{
+ configuration.d->peerCertificate = peerCert;
+}
+
+/*!
+ \internal
+ Stores \a peerChain in the \a configuration.
+*/
+void QTlsBackend::storePeerCertificateChain(QSslConfiguration &configuration,
+ const QList<QSslCertificate> &peerChain)
+{
+ configuration.d->peerCertificateChain = peerChain;
+}
+
+/*!
+ \internal
+ Clears the peer certificate chain in \a configuration.
+*/
+void QTlsBackend::clearPeerCertificates(QSslConfiguration &configuration)
+{
+ configuration.d->peerCertificate.clear();
+ configuration.d->peerCertificateChain.clear();
+}
+
+/*!
+ \internal
+ Clears the peer certificate chain in \a d.
+*/
+void QTlsBackend::clearPeerCertificates(QSslSocketPrivate *d)
+{
+ Q_ASSERT(d);
+ d->configuration.peerCertificate.clear();
+ d->configuration.peerCertificateChain.clear();
+}
+
+/*!
+ \internal
+ Updates the configuration in \a d with \a shared value.
+*/
+void QTlsBackend::setPeerSessionShared(QSslSocketPrivate *d, bool shared)
+{
+ Q_ASSERT(d);
+ d->configuration.peerSessionShared = shared;
+}
+
+/*!
+ \internal
+ Sets TLS session in \a d to \a asn1.
+*/
+void QTlsBackend::setSessionAsn1(QSslSocketPrivate *d, const QByteArray &asn1)
+{
+ Q_ASSERT(d);
+ d->configuration.sslSession = asn1;
+}
+
+/*!
+ \internal
+ Sets TLS session lifetime hint in \a d to \a hint.
+*/
+void QTlsBackend::setSessionLifetimeHint(QSslSocketPrivate *d, int hint)
+{
+ Q_ASSERT(d);
+ d->configuration.sslSessionTicketLifeTimeHint = hint;
+}
+
+/*!
+ \internal
+ Sets application layer protocol negotiation status in \a d to \a st.
+*/
+void QTlsBackend::setAlpnStatus(QSslSocketPrivate *d, AlpnNegotiationStatus st)
+{
+ Q_ASSERT(d);
+ d->configuration.nextProtocolNegotiationStatus = st;
+}
+
+/*!
+ \internal
+ Sets \a protocol in \a d as a negotiated application layer protocol.
+*/
+void QTlsBackend::setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol)
+{
+ Q_ASSERT(d);
+ d->configuration.nextNegotiatedProtocol = protocol;
+}
+
+/*!
+ \internal
+ Stores \a peerCert in the TLS configuration of \a d.
+*/
+void QTlsBackend::storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert)
+{
+ Q_ASSERT(d);
+ d->configuration.peerCertificate = peerCert;
+}
+
+/*!
+ \internal
+
+ Stores \a peerChain in the TLS configuration of \a d.
+
+ \note This is a helper function that TlsCryptograph and DtlsCryptograph
+ call during a handshake.
+*/
+void QTlsBackend::storePeerCertificateChain(QSslSocketPrivate *d,
+ const QList<QSslCertificate> &peerChain)
+{
+ Q_ASSERT(d);
+ d->configuration.peerCertificateChain = peerChain;
+}
+
+/*!
+ \internal
+
+ Adds \a rootCert to the list of trusted root certificates in \a d.
+
+ \note In Qt 6.1 it's only used on Windows, during so called 'CA fetch'.
+*/
+void QTlsBackend::addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert)
+{
+ Q_ASSERT(d);
+ if (!d->configuration.caCertificates.contains(rootCert))
+ d->configuration.caCertificates += rootCert;
+}
+
+/*!
+ \internal
+
+ Saves ephemeral \a key in \a d.
+
+ \sa QSslConfiguration::ephemeralKey()
+*/
+void QTlsBackend::setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key)
+{
+ Q_ASSERT(d);
+ d->configuration.ephemeralServerKey = key;
+}
+
+/*!
+ \internal
+
+ Implementation-specific. Sets the security level suitable for Qt's
+ auto-tests.
+*/
+void QTlsBackend::forceAutotestSecurityLevel()
+{
+}
+
+#endif // QT_CONFIG(ssl)
+
+namespace QTlsPrivate {
+
+/*!
+ \internal (Network-private)
+ \namespace QTlsPrivate
+ \brief Namespace containing onternal types that TLS backends implement.
+
+ This namespace is private to Qt and the backends that implement its TLS support.
+*/
+
+/*!
+ \class TlsKey
+ \internal (Network-private)
+ \brief TlsKey is an abstract class, that allows a TLS plugin to provide
+ an underlying implementation for the class QSslKey.
+
+ Most functions in the class TlsKey are pure virtual and thus have to be
+ reimplemented by a TLS backend that supports QSslKey. In many cases an
+ empty implementation as an overrider is sufficient, albeit with some
+ of QSslKey's functionality missing.
+
+ \sa QTlsBackend::createKey(), QTlsBackend::implementedClasses(), QSslKey
+*/
+
+/*!
+ \fn void TlsKey::decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, const QByteArray &passPhrase, bool deepClear)
+ \internal
+
+ If a support of public and private keys in DER format is required, this function
+ must be overridden and should initialize this key using the \a type, \a algorithm, \a der
+ and \a passPhrase. If this key was initialized previously, \a deepClear
+ has an implementation-specific meaning (e.g., if an implementation is using
+ reference-counting and can share internally some data structures, a value \c true may
+ trigger decrementing a reference counter on some implementation-specific object).
+
+ \note An empty overrider is sufficient, but then reading keys in QSsl::Der format
+ will not be supported.
+
+ \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm
+*/
+
+/*!
+ \fn void TlsKey::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, const QByteArray &passPhrase, bool deepClear)
+ \internal
+
+ If a support of public and private keys in PEM format is required, this function must
+ be overridden and should initialize this key using the \a type, \a algorithm, \a pem and
+ \a passPhrase. If this key was initialized previously, \a deepClear has an
+ implementation-specific meaning (e.g., in an implementation using reference-counting,
+ a value \c true may trigger decrementing a reference counter on some implementation-specific
+ object).
+
+ \note An empty overrider is sufficient, but then reading keys in QSsl::Pem format
+ will not be supported.
+
+ \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm
+*/
+
+/*!
+ \fn QByteArray TlsKey::toPem(const QByteArray &passPhrase) const
+ \internal
+
+ This function must be overridden, if converting a key to PEM format, potentially with
+ encryption, is needed (e.g. to save a QSslKey into a file). If this key is
+ private and \a passPhrase is not empty, the key's data is expected to be encrypted using
+ some conventional encryption algorithm (e.g. DES or AES - the one that different tools
+ or even the class QSslKey can understand later).
+
+ \note If this particular functionality is not needed, an overrider returning an
+ empty QByteArray is sufficient.
+
+ \sa QSslKey::toPem()
+*/
+
+/*!
+ \fn QByteArray TlsKey::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
+ \internal
+
+ Converts \a pem to DER format, using this key's type and algorithm. The parameter \a headers
+ must be a valid, non-null pointer. When parsing \a pem, the headers found there will be saved
+ into \a headers.
+
+ \note An overrider returning an empty QByteArray is sufficient, if QSslKey::toDer() is not
+ needed.
+
+ \note This function is very implementation-specific. A backend, that already has this key's
+ non-empty DER data, may simply return this data.
+
+ \sa QSslKey::toDer()
+*/
+
+/*!
+ \fn QByteArray TlsKey::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
+ \internal
+
+ If overridden, this function is expected to convert \a der, using \a headers, to PEM format.
+
+ \note This function is very implementation-specific. As of now (Qt 6.1), it is only required by
+ Qt's own non-OpenSSL backends, that internally use DER and implement QSslKey::toPem()
+ via pemFromDer().
+*/
+
+/*!
+ \fn void TlsKey::fromHandle(Qt::HANDLE handle, KeyType type)
+ \internal
+
+ Initializes this key using the \a handle and \a type, taking the ownership
+ of the \a handle.
+
+ \note The meaning of the \a handle is implementation-specific.
+
+ \note If a TLS backend does not support such keys, it must provide an
+ empty implementation.
+
+ \sa handle(), QSslKey::QSslKey(), QSslKet::handle()
+*/
+
+/*!
+ \fn TlsKey::handle() const
+ \internal
+
+ If a TLS backend supports opaque keys, returns a native handle that
+ this key was initialized with.
+
+ \sa fromHandle(), QSslKey::handle()
+*/
+
+/*!
+ \fn bool TlsKey::isNull() const
+ \internal
+
+ Returns \c true if this is a null key, \c false otherwise.
+
+ \note A null key corresponds to the default-constructed
+ QSslKey or the one, that was cleared via QSslKey::clear().
+
+ \sa QSslKey::isNull()
+*/
+
+/*!
+ \fn QSsl::KeyType TlsKey::type() const
+ \internal
+
+ Returns the type of this key (public or private).
+*/
+
+/*!
+ \fn QSsl::KeyAlgorithm TlsKey::algorithm() const
+ \internal
+
+ Return this key's algorithm.
+*/
+
+/*!
+ \fn int TlsKey::length() const
+ \internal
+
+ Returns the length of the key in bits, or -1 if the key is null.
+*/
+
+/*!
+ \fn void TlsKey::clear(bool deep)
+ \internal
+
+ Clears the contents of this key, making it a null key. The meaning
+ of \a deep is implementation-specific (e.g. if some internal objects
+ representing a key can be shared using reference counting, \a deep equal
+ to \c true would imply decrementing a reference count).
+
+ \sa isNull()
+*/
+
+/*!
+ \fn bool TlsKey::isPkcs8() const
+ \internal
+
+ This function is internally used only by Qt's own TLS plugins and affects
+ the way PEM file is generated by TlsKey. It's sufficient to override it
+ and return \c false in case a new TLS backend is not using Qt's plugin
+ as a base.
+*/
+
+/*!
+ \fn QByteArray TlsKey::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const
+ \internal
+
+ This function allows to decrypt \a data (for example, a private key read from a file), using
+ \a passPhrase, initialization vector \a iv. \a cipher is describing a block cipher and its
+ mode (for example, AES256 + CBC). decrypt() is needed to implement QSslKey's constructor.
+
+ \note A TLS backend may provide an empty implementation, but as a result QSslKey will not be able
+ to work with private encrypted keys.
+
+ \sa QSslKey
+*/
+
+/*!
+ \fn QByteArray TlsKey::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const
+ \internal
+
+ This function is needed to implement QSslKey::toPem() with encryption (for a private
+ key). \a cipher names a block cipher to use to encrypt \a data, using
+ \a passPhrase and initialization vector \a iv.
+
+ \note An empty implementation is sufficient, but QSslKey::toPem() will fail for
+ a private key and non-empty passphrase.
+
+ \sa QSslKey
+*/
+
+/*!
+ \internal
+
+ Destroys this key.
+*/
+TlsKey::~TlsKey() = default;
+
+/*!
+ \internal
+
+ A convenience function that returns a string, corresponding to the
+ key type or algorithm, which can be used as a header in a PEM file.
+*/
+QByteArray TlsKey::pemHeader() const
+{
+ if (type() == QSsl::PublicKey)
+ return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
+ else if (algorithm() == QSsl::Rsa)
+ return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dsa)
+ return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Ec)
+ return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dh)
+ return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
+
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*!
+ \internal
+ A convenience function that returns a string, corresponding to the
+ key type or algorithm, which can be used as a footer in a PEM file.
+*/
+QByteArray TlsKey::pemFooter() const
+{
+ if (type() == QSsl::PublicKey)
+ return QByteArrayLiteral("-----END PUBLIC KEY-----");
+ else if (algorithm() == QSsl::Rsa)
+ return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dsa)
+ return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Ec)
+ return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
+ else if (algorithm() == QSsl::Dh)
+ return QByteArrayLiteral("-----END PRIVATE KEY-----");
+
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*!
+ \class X509Certificate
+ \internal (Network-private)
+ \brief X509Certificate is an abstract class that allows a TLS backend to
+ provide an implementation of the QSslCertificate class.
+
+ This class provides an interface that must be reimplemented by a TLS plugin,
+ that supports QSslCertificate. Most functions are pure virtual, and thus
+ have to be overridden. For some of them, an empty overrider is acceptable,
+ though a part of functionality in QSslCertificate will be missing.
+
+ \sa QTlsBackend::createCertificate(), QTlsBackend::X509PemReader(), QTlsBackend::X509DerReader()
+*/
+
+/*!
+ \fn bool X509Certificate::isEqual(const X509Certificate &other) const
+ \internal
+
+ This function is expected to return \c true if this certificate is the same as
+ the \a other, \c false otherwise. Used by QSslCertificate's comparison operators.
+*/
+
+/*!
+ \fn bool X509Certificate::isNull() const
+ \internal
+
+ Returns true if this certificate was default-constructed and not initialized yet.
+ This function is called by QSslCertificate::isNull().
+
+ \sa QSslCertificate::isNull()
+*/
+
+/*!
+ \fn bool X509Certificate::isSelfSigned() const
+ \internal
+
+ This function is needed to implement QSslCertificate::isSelfSigned()
+
+ \sa QSslCertificate::isSelfSigned()
+*/
+
+/*!
+ \fn QByteArray X509Certificate::version() const
+ \internal
+
+ Implements QSslCertificate::version().
+
+ \sa QSslCertificate::version()
+*/
+
+/*!
+ \fn QByteArray X509Certificate::serialNumber() const
+ \internal
+
+ This function is expected to return the certificate's serial number string in
+ hexadecimal format.
+
+ \sa QSslCertificate::serialNumber()
+*/
+
+/*!
+ \fn QStringList X509Certificate::issuerInfo(QSslCertificate::SubjectInfo subject) const
+ \internal
+
+ This function is expected to return the issuer information for the \a subject
+ from the certificate, or an empty list if there is no information for subject
+ in the certificate. There can be more than one entry of each type.
+
+ \sa QSslCertificate::issuerInfo().
+*/
+
+/*!
+ \fn QStringList X509Certificate::issuerInfo(const QByteArray &attribute) const
+ \internal
+
+ This function is expected to return the issuer information for attribute from
+ the certificate, or an empty list if there is no information for \a attribute
+ in the certificate. There can be more than one entry for an attribute.
+
+ \sa QSslCertificate::issuerInfo().
+*/
+
+/*!
+ \fn QStringList X509Certificate::subjectInfo(QSslCertificate::SubjectInfo subject) const
+ \internal
+
+ This function is expected to return the information for the \a subject, or an empty list
+ if there is no information for subject in the certificate. There can be more than one
+ entry of each type.
+
+ \sa QSslCertificate::subjectInfo().
+*/
+
+/*!
+ \fn QStringList X509Certificate::subjectInfo(const QByteArray &attribute) const
+ \internal
+
+ This function is expected to return the subject information for \a attribute, or
+ an empty list if there is no information for attribute in the certificate.
+ There can be more than one entry for an attribute.
+
+ \sa QSslCertificate::subjectInfo().
+*/
+
+/*!
+ \fn QList<QByteArray> X509Certificate::subjectInfoAttributes() const
+ \internal
+
+ This function is expected to return a list of the attributes that have values
+ in the subject information of this certificate. The information associated
+ with a given attribute can be accessed using the subjectInfo() method. Note
+ that this list may include the OIDs for any elements that are not known by
+ the TLS backend.
+
+ \note This function is needed for QSslCertificate:::subjectInfoAttributes().
+
+ \sa subjectInfo()
+*/
+
+/*!
+ \fn QList<QByteArray> X509Certificate::issuerInfoAttributes() const
+ \internal
+
+ This function is expected to return a list of the attributes that have
+ values in the issuer information of this certificate. The information
+ associated with a given attribute can be accessed using the issuerInfo()
+ method. Note that this list may include the OIDs for any
+ elements that are not known by the TLS backend.
+
+ \note This function implements QSslCertificate::issuerInfoAttributes().
+
+ \sa issuerInfo()
+*/
+
+/*!
+ \fn QMultiMap<QSsl::AlternativeNameEntryType, QString> X509Certificate::subjectAlternativeNames() const
+ \internal
+
+ This function is expected to return the list of alternative subject names for
+ this certificate. The alternative names typically contain host names, optionally
+ with wildcards, that are valid for this certificate.
+
+ \sa subjectInfo()
+*/
+
+/*!
+ \fn QDateTime X509Certificate::effectiveDate() const
+ \internal
+
+ This function is expected to return the date-time that the certificate
+ becomes valid, or an empty QDateTime if this is a null certificate.
+
+ \sa expiryDate()
+*/
+
+/*!
+ \fn QDateTime X509Certificate::expiryDate() const
+ \internal
+
+ This function is expected to return the date-time that the certificate expires,
+ or an empty QDateTime if this is a null certificate.
+
+ \sa effectiveDate()
+*/
+
+/*!
+ \fn qsizetype X509Certificate::numberOfExtensions() const
+ \internal
+
+ This function is expected to return the number of X509 extensions of
+ this certificate.
+*/
+
+/*!
+ \fn QString X509Certificate::oidForExtension(qsizetype i) const
+ \internal
+
+ This function is expected to return the ASN.1 OID for the extension
+ with index \a i.
+
+ \sa numberOfExtensions()
+*/
+
+/*!
+ \fn QString X509Certificate::nameForExtension(qsizetype i) const
+ \internal
+
+ This function is expected to return the name for the extension
+ with index \a i. If no name is known for the extension then the
+ OID will be returned.
+
+ \sa numberOfExtensions(), oidForExtension()
+*/
+
+/*!
+ \fn QVariant X509Certificate::valueForExtension(qsizetype i) const
+ \internal
+
+ This function is expected to return the value of the extension
+ with index \a i. The structure of the value returned depends on
+ the extension type
+
+ \sa numberOfExtensions()
+*/
+
+/*!
+ \fn bool X509Certificate::isExtensionCritical(qsizetype i) const
+ \internal
+
+ This function is expected to return the criticality of the extension
+ with index \a i.
+
+ \sa numberOfExtensions()
+*/
+
+/*!
+ \fn bool X509Certificate::isExtensionSupported(qsizetype i) const
+ \internal
+
+ This function is expected to return \c true if this extension is supported.
+ In this case, supported simply means that the structure of the QVariant returned
+ by the valueForExtension() accessor will remain unchanged between versions.
+
+ \sa numberOfExtensions()
+*/
+
+/*!
+ \fn QByteArray X509Certificate::toPem() const
+ \internal
+
+ This function is expected to return this certificate converted to a PEM (Base64)
+ encoded representation.
+*/
+
+/*!
+ \fn QByteArray X509Certificate::toDer() const
+ \internal
+
+ This function is expected to return this certificate converted to a DER (binary)
+ encoded representation.
+*/
+
+/*!
+ \fn QString X509Certificate::toText() const
+ \internal
+
+ This function is expected to return this certificate converted to a human-readable
+ text representation.
+*/
+
+/*!
+ \fn Qt::HANDLE X509Certificate::handle() const
+ \internal
+
+ This function is expected to return a pointer to the native certificate handle,
+ if there is one, else nullptr.
+*/
+
+/*!
+ \fn size_t X509Certificate::hash(size_t seed) const
+ \internal
+
+ This function is expected to return the hash value for this certificate,
+ using \a seed to seed the calculation.
+*/
+
+/*!
+ \internal
+
+ Destroys this certificate.
+*/
+X509Certificate::~X509Certificate() = default;
+
+/*!
+ \internal
+
+ Returns the certificate subject's public key.
+*/
+TlsKey *X509Certificate::publicKey() const
+{
+ return nullptr;
+}
+
+#if QT_CONFIG(ssl)
+
+/*!
+ \class TlsCryptograph
+ \internal (Network-private)
+ \brief TlsCryptograph is an abstract class, that allows a TLS plugin to implement QSslSocket.
+
+ This abstract base class provides an interface that must be reimplemented by a TLS plugin,
+ that supports QSslSocket. A class, implementing TlsCryptograph's interface, is responsible
+ for TLS handshake, reading and writing encryped application data; it is expected
+ to work with QSslSocket and it's private implementation - QSslSocketPrivate.
+ QSslSocketPrivate provides access to its read/write buffers, QTcpSocket it
+ internally uses for connecting, reading and writing. QSslSocketPrivate
+ can also be used for reporting errors and storing the certificates received
+ during the handshake phase.
+
+ \note Most of the functions in this class are pure virtual and have no actual implementation
+ in the QtNetwork module. This documentation is mostly conceptual and only describes what those
+ functions are expected to do, but not how they must be implemented.
+
+ \sa QTlsBackend::createTlsCryptograph()
+*/
+
+/*!
+ \fn void TlsCryptograph::init(QSslSocket *q, QSslSocketPrivate *d)
+ \internal
+
+ When initializing this TlsCryptograph, QSslSocket will pass a pointer to self and
+ its d-object using this function.
+*/
+
+/*!
+ \fn QList<QSslError> TlsCryptograph::tlsErrors() const
+ \internal
+
+ Returns a list of QSslError, describing errors encountered during
+ the TLS handshake.
+
+ \sa QSslSocket::sslHandshakeErrors()
+*/
+
+/*!
+ \fn void TlsCryptograph::startClientEncryption()
+ \internal
+
+ A client-side QSslSocket calls this function after its internal TCP socket
+ establishes a connection with a remote host, or from QSslSocket::startClientEncryption().
+ This TlsCryptograph is expected to initialize some implementation-specific TLS context,
+ if needed, and then start the client side of the TLS handshake (for example, by calling
+ transmit()), using TCP socket from QSslSocketPrivate.
+
+ \sa init(), transmit(), QSslSocket::startClientEncryption(), QSslSocket::connectToHostEncrypted()
+*/
+
+/*!
+ \fn void TlsCryptograph::startServerEncryption()
+ \internal
+
+ This function is called by QSslSocket::startServerEncryption(). The TlsCryptograph
+ is expected to initialize some implementation-specific TLS context, if needed,
+ and then try to read the ClientHello message and continue the TLS handshake
+ (for example, by calling transmit()).
+
+ \sa transmit(), QSslSocket::startServerEncryption()
+*/
+
+/*!
+ \fn void TlsCryptograph::continueHandshake()
+ \internal
+
+ QSslSocket::resume() calls this function if its pause mode is QAbstractSocket::PauseOnSslErrors,
+ and errors, found during the handshake, were ignored. If implemented, this function is expected
+ to emit QSslSocket::encrypted().
+
+ \sa QAbstractSocket::pauseMode(), QSslSocket::sslHandshakeErrors(), QSslSocket::ignoreSslErrors(), QSslSocket::resume()
+*/
+
+/*!
+ \fn void TlsCryptograph::disconnectFromHost()
+ \internal
+
+ This function is expected to call disconnectFromHost() on the TCP socket
+ that can be obtained from QSslSocketPrivate. Any additional actions
+ are implementation-specific (e.g., sending shutdown alert message).
+
+*/
+
+/*!
+ \fn void TlsCryptograph::disconnected()
+ \internal
+
+ This function is called when the remote has disconnected. If there
+ is data left to be read you may ignore the maxReadBufferSize restriction
+ and read it all now.
+*/
+
+/*!
+ \fn QSslCipher TlsCryptograph::sessionCipher() const
+ \internal
+
+ This function returns a QSslCipher object describing the ciphersuite negotiated
+ during the handshake.
+*/
+
+/*!
+ \fn QSsl::SslProtocol TlsCryptograph::sessionProtocol() const
+ \internal
+
+ This function returns the version of TLS (or DTLS) protocol negotiated during the handshake.
+*/
+
+/*!
+ \fn void TlsCryptograph::transmit()
+ \internal
+
+ This function is responsible for reading and writing data. The meaning of these I/O
+ operations depends on an implementation-specific TLS state machine. These read and write
+ operations can be reading and writing parts of a TLS handshake (e.g. by calling handshake-specific
+ functions), or reading and writing application data (if encrypted connection was already
+ established). transmit() is expected to use the QSslSocket's TCP socket (accessible via
+ QSslSocketPrivate) to read the incoming data and write the outgoing data. When in encrypted
+ state, transmit() is also using QSslSocket's internal read and write buffers: the read buffer
+ to fill with decrypted incoming data; the write buffer - for the data to encrypt and send.
+ This TlsCryptograph can also use QSslSocketPrivate to check which TLS errors were ignored during
+ the handshake.
+
+ \note This function is responsible for emitting QSslSocket's signals, that occur during the
+ handshake (e.g. QSslSocket::sslErrors() or QSslSocket::encrypted()), and also read/write signals,
+ e.g. QSslSocket::bytesWritten() and QSslSocket::readyRead().
+
+ \sa init()
+*/
+
+/*!
+ \internal
+
+ Destroys this object.
+*/
+TlsCryptograph::~TlsCryptograph() = default;
+
+/*!
+ \internal
+
+ This function allows to share QSslContext between several QSslSocket objects.
+ The default implementation does nothing.
+
+ \note The definition of the class QSslContext is implementation-specific.
+
+ \sa sslContext()
+*/
+void TlsCryptograph::checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext)
+{
+ Q_UNUSED(tlsContext);
+}
+
+/*!
+ \internal
+
+ Returns the context previously set by checkSettingSslContext() or \nullptr,
+ if no context was set. The default implementation returns \nullptr.
+
+ \sa checkSettingSslContext()
+*/
+std::shared_ptr<QSslContext> TlsCryptograph::sslContext() const
+{
+ return {};
+}
+
+/*!
+ \internal
+
+ If this TLS backend supports reporting errors before handshake is finished,
+ e.g. from a verification callback function, enableHandshakeContinuation()
+ allows this object to continue handshake. The default implementation does
+ nothing.
+
+ \sa QSslSocket::handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError()
+*/
+void TlsCryptograph::enableHandshakeContinuation()
+{
+}
+
+/*!
+ \internal
+
+ Windows and OpenSSL-specific, only used internally by Qt's OpenSSL TLS backend.
+
+ \note The default empty implementation is sufficient.
+*/
+void TlsCryptograph::cancelCAFetch()
+{
+}
+
+/*!
+ \internal
+
+ Windows and Schannel-specific, only used by Qt's Schannel TLS backend, in
+ general, if a backend has its own buffer where it stores undecrypted data
+ then it must report true if it contains any data through this function.
+
+ \note The default empty implementation, returning \c false is sufficient.
+*/
+bool TlsCryptograph::hasUndecryptedData() const
+{
+ return false;
+}
+
+/*!
+ \internal
+
+ Returns the list of OCSP (Online Certificate Status Protocol) responses,
+ received during the handshake. The default implementation returns an empty
+ list.
+*/
+QList<QOcspResponse> TlsCryptograph::ocsps() const
+{
+ return {};
+}
+
+/*!
+ \internal
+
+ A helper function that can be used during a handshake. Returns \c true if the \a peerName
+ matches one of subject alternative names or common names found in the \a certificate.
+*/
+bool TlsCryptograph::isMatchingHostname(const QSslCertificate &certificate, const QString &peerName)
+{
+ return QSslSocketPrivate::isMatchingHostname(certificate, peerName);
+}
+
+/*!
+ \internal
+ Calls QAbstractSocketPrivate::setErrorAndEmit() for \a d, passing \a errorCode and
+ \a errorDescription as parameters.
+*/
+void TlsCryptograph::setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode,
+ const QString &errorDescription) const
+{
+ Q_ASSERT(d);
+ d->setErrorAndEmit(errorCode, errorDescription);
+}
+
+#if QT_CONFIG(dtls)
+/*!
+ \class DtlsBase
+ \internal (Network-private)
+ \brief DtlsBase is a base class for the classes DtlsCryptograph and DtlsCookieVerifier.
+
+ DtlsBase is the base class for the classes DtlsCryptograph and DtlsCookieVerifier. It's
+ an abstract class, an interface that these before-mentioned classes share. It allows to
+ set, get and clear the last error that occurred, set and get cookie generation parameters,
+ set and get QSslConfiguration.
+
+ \note This class is not supposed to be inherited directly, it's only needed by DtlsCryptograph
+ and DtlsCookieVerifier.
+
+ \sa QDtls, QDtlsClientVerifier, DtlsCryptograph, DtlsCookieVerifier
+*/
+
+/*!
+ \fn void DtlsBase::setDtlsError(QDtlsError code, const QString &description)
+ \internal
+
+ Sets the last error to \a code and its textual description to \a description.
+
+ \sa QDtlsError, error(), errorString()
+*/
+
+/*!
+ \fn QDtlsError DtlsBase::error() const
+ \internal
+
+ This function, when overridden, is expected to return the code for the last error that occurred.
+ If no error occurred it should return QDtlsError::NoError.
+
+ \sa QDtlsError, errorString(), setDtlsError()
+*/
+
+/*!
+ \fn QDtlsError DtlsBase::errorString() const
+ \internal
+
+ This function, when overridden, is expected to return the textual description for the last error
+ that occurred or an empty string if no error occurred.
+
+ \sa QDtlsError, error(), setDtlsError()
+*/
+
+/*!
+ \fn void DtlsBase::clearDtlsError()
+ \internal
+
+ This function is expected to set the error code for the last error to QDtlsError::NoError and
+ its textual description to an empty string.
+
+ \sa QDtlsError, setDtlsError(), error(), errorString()
+*/
+
+/*!
+ \fn void DtlsBase::setConfiguration(const QSslConfiguration &configuration)
+ \internal
+
+ Sets a TLS configuration that an object of a class inheriting from DtlsCookieVerifier or
+ DtlsCryptograph will use, to \a configuration.
+
+ \sa configuration()
+*/
+
+/*!
+ \fn QSslConfiguration DtlsBase::configuration() const
+ \internal
+
+ Returns TLS configuration this object is using (either set by setConfiguration()
+ previously, or the default DTLS configuration).
+
+ \sa setConfiguration(), QSslConfiguration::defaultDtlsConfiguration()
+*/
+
+/*!
+ \fn bool DtlsBase::setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters &params)
+ \internal
+
+ Sets the DTLS cookie generation parameters that DtlsCookieVerifier or DtlsCryptograph will use to
+ \a params.
+
+ \note This function returns \c false if parameters were invalid - if the secret was empty. Otherwise,
+ this function must return true.
+
+ \sa QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters()
+*/
+
+/*!
+ \fn QDtlsClientVerifier::GeneratorParameters DtlsBase::cookieGeneratorParameters() const
+ \internal
+
+ Returns DTLS cookie generation parameters that were either previously set by setCookieGeneratorParameters(),
+ or default parameters.
+
+ \sa setCookieGeneratorParameters()
+*/
+
+/*!
+ \internal
+
+ Destroys this object.
+*/
+DtlsBase::~DtlsBase() = default;
+
+/*!
+ \class DtlsCookieVerifier
+ \internal (Network-private)
+ \brief DtlsCookieVerifier is an interface that allows a TLS plugin to support the class QDtlsClientVerifier.
+
+ DtlsCookieVerifier is an interface, an abstract class, that has to be implemented by
+ a TLS plugin that supports DTLS cookie verification.
+
+ \sa QDtlsClientVerifier
+*/
+
+/*!
+ \fn bool DtlsCookieVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)
+ \internal
+
+ This function is expected to verify a ClientHello message, found in \a dgram, using \a address,
+ \a port, and cookie generator parameters. The function returns \c true if such cookie was found
+ and \c false otherwise. If no valid cookie was found in the \a dgram, this verifier should use
+ \a socket to send a HelloVerifyRequest message, using \a address and \a port as the destination
+ and a source material for cookie generation, see also
+ \l {RFC 6347, section 4.2.1}
+
+ \sa QDtlsClientVerifier
+*/
+
+/*!
+ \fn QByteArray DtlsCookieVerifier::verifiedHello() const
+ \internal
+
+ Returns the last ClientHello message containing the DTLS cookie that this verifier was
+ able to verify as correct, or an empty byte array.
+
+ \sa verifyClient()
+*/
+
+/*!
+ \class DtlsCryptograph
+ \internal (Network-private)
+ \brief DtlsCryptograph is an interface that allows a TLS plugin to implement the class QDtls.
+
+ DtlsCryptograph is an abstract class; a TLS plugin can provide a class, inheriting from
+ DtlsCryptograph and implementing its pure virtual functions, thus implementing the class
+ QDtls and enabling DTLS over UDP.
+
+ To write DTLS datagrams, a class, inheriting DtlsCryptograph, is expected to use
+ QUdpSocket. In general, all reading is done externally, so DtlsCryptograph is
+ expected to only write into QUdpSocket, check possible socket errors, change socket
+ options if needed.
+
+ \note All functions in this class are pure virtual and have no actual implementation
+ in the QtNetwork module. This documentation is mostly conceptual and only describes
+ what those functions are expected to do, but not how they must be implemented.
+
+ \sa QDtls, QUdpSocket
+*/
+
+/*!
+ \fn QSslSocket::SslMode DtlsCryptograph::cryptographMode() const
+ \internal
+
+ Returns the mode (client or server) this object operates in.
+
+ \note This mode is set once when a new DtlsCryptograph is created
+ by QTlsBackend and cannot change.
+
+ \sa QTlsBackend::createDtlsCryptograph()
+*/
+
+/*!
+ \fn void DtlsCryptograph::setPeer(const QHostAddress &addr, quint16 port, const QString &name)
+ \internal
+
+ Sets the remote peer's address to \a addr and remote port to \a port. \a name,
+ if not empty, is to be used when validating the peer's certificate.
+
+ \sa peerAddress(), peerPort(), peerVerificationName()
+*/
+
+/*!
+ \fn QHostAddress DtlsCryptograph::peerAddress() const
+ \internal
+
+ Returns the remote peer's address previously set by setPeer() or,
+ if no address was set, an empty address.
+
+ \sa setPeer()
+*/
+
+/*!
+ \fn quint16 DtlsCryptograph::peerPort() const
+ \internal
+
+ Returns the remote peer's port previously set by setPeer() or
+ 0 if no port was set.
+
+ \sa setPeer(), peerAddress()
+*/
+
+/*!
+ \fn void DtlsCryptograph::setPeerVerificationName(const QString &name)
+ \internal
+
+ Sets the host name to use during certificate validation to \a name.
+
+ \sa peerVerificationName(), setPeer()
+*/
+
+/*!
+ \fn QString DtlsCryptograph::peerVerificationName() const
+ \internal
+
+ Returns the name that this object is using during the certificate validation,
+ previously set by setPeer() or setPeerVerificationName(). Returns an empty string
+ if no peer verification name was set.
+
+ \sa setPeer(), setPeerVerificationName()
+*/
+
+/*!
+ \fn void DtlsCryptograph::setDtlsMtuHint(quint16 mtu)
+ \internal
+
+ Sets the maximum transmission unit (MTU), if it is supported by a TLS implementation, to \a mtu.
+
+ \sa dtlsMtuHint()
+*/
+
+/*!
+ \fn quint16 DtlsCryptograph::dtlsMtuHint() const
+ \internal
+
+ Returns the value of the maximum transmission unit either previously set by setDtlsMtuHint(),
+ or some implementation-specific value (guessed or somehow known to this DtlsCryptograph).
+
+ \sa setDtlsMtuHint()
+*/
+
+/*!
+ \fn QDtls::HandshakeState DtlsCryptograph::state() const
+ \internal
+
+ Returns the current handshake state for this DtlsCryptograph (not started, in progress,
+ peer verification error found, complete).
+
+ \sa isConnectionEncrypted(), startHandshake()
+*/
+
+/*!
+ \fn bool DtlsCryptograph::isConnectionEncrypted() const
+ \internal
+
+ Returns \c true if this DtlsCryptograph has completed a handshake without validation
+ errors (or these errors were ignored). Returns \c false otherwise.
+*/
+
+/*!
+ \fn bool DtlsCryptograph::startHandshake(QUdpSocket *socket, const QByteArray &dgram)
+ \internal
+
+ This function is expected to initialize some implementation-specific context and to start a DTLS
+ handshake, using \a socket to write datagrams (but not to read them). If this object is operating
+ as a server, \a dgram is non-empty and contains the ClientHello message. This function returns
+ \c true if no error occurred (and this DtlsCryptograph's state switching to
+ QDtls::HandshakeState::HandshakeInProgress), \c false otherwise.
+
+ \sa continueHandshake(), handleTimeout(), resumeHandshake(), abortHandshake(), state()
+*/
+
+/*!
+ \fn bool DtlsCryptograph::handleTimeout(QUdpSocket *socket)
+ \internal
+
+ In case a timeout occurred during the handshake, allows to re-transmit the last message,
+ using \a socket to write the datagram. Returns \c true if no error occurred, \c false otherwise.
+
+ \sa QDtls::handshakeTimeout(), QDtls::handleTimeout()
+*/
+
+/*!
+ \fn bool DtlsCryptograph::continueHandshake(QUdpSocket *socket, const QByteArray &dgram)
+ \internal
+
+ Continues the handshake, using \a socket to write datagrams (a handshake-specific message).
+ \a dgram contains the peer's handshake-specific message. Returns \c false in case some error
+ was encountered (this can include socket-related errors and errors found during the certificate
+ validation). Returns \c true if the handshake was complete successfully, or is still in progress.
+
+ This function, depending on the implementation-specific state machine, may leave the handshake
+ state in QDtls::HandshakeState::HandshakeInProgress, or switch to QDtls::HandshakeState::HandshakeComplete
+ or QDtls::HandshakeState::PeerVerificationFailed.
+
+ This function may store the peer's certificate (or chain of certificates), extract and store
+ the information about the negotiated session protocol and ciphersuite.
+
+ \sa startHandshake()
+*/
+
+/*!
+ \fn bool DtlsCryptograph::resumeHandshake(QUdpSocket *socket)
+ \internal
+
+ If peer validation errors were found duing the handshake, this function tries to
+ continue and complete the handshake. If errors were ignored, the function switches
+ this object's state to QDtls::HandshakeState::HandshakeComplete and returns \c true.
+
+ \sa abortHandshake()
+*/
+
+/*!
+ \fn void DtlsCryptograph::abortHandshake(QUdpSocket *socket)
+ \internal
+
+ Aborts the handshake if it's in progress or in the state QDtls::HandshakeState::PeerVerificationFailed.
+ The use of \a socket is implementation-specific (for example, this DtlsCryptograph may send
+ ShutdownAlert message).
+
+ \sa resumeHandshake()
+*/
+
+/*!
+ \fn void DtlsCryptograph::sendShutdownAlert(QUdpSocket *socket)
+ \internal
+
+ If the underlying TLS library provides the required functionality, this function
+ may sent ShutdownAlert message using \a socket.
+*/
+
+/*!
+ \fn QList<QSslError> DtlsCryptograph::peerVerificationErrors() const
+ \internal
+
+ Returns the list of errors that this object encountered during DTLS handshake
+ and certificate validation.
+
+ \sa ignoreVerificationErrors()
+*/
+
+/*!
+ \fn void DtlsCryptograph::ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore)
+ \internal
+
+ Tells this object to ignore errors from \a errorsToIgnore when they are found during
+ DTLS handshake.
+
+ \sa peerVerificationErrors()
+*/
+
+/*!
+ \fn QSslCipher DtlsCryptograph::dtlsSessionCipher() const
+ \internal
+
+ If such information is available, returns the ciphersuite, negotiated during
+ the handshake.
+
+ \sa continueHandshake(), dtlsSessionProtocol()
+*/
+
+/*!
+ \fn QSsl::SslProtocol DtlsCryptograph::dtlsSessionProtocol() const
+ \internal
+
+ Returns the version of the session protocol that was negotiated during the handshake or
+ QSsl::UnknownProtocol if the handshake is incomplete or no information about the session
+ protocol is available.
+
+ \sa continueHandshake(), dtlsSessionCipher()
+*/
+
+/*!
+ \fn qint64 DtlsCryptograph::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram)
+ \internal
+
+ If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, this function
+ encrypts \a dgram and writes this encrypted data into \a socket.
+
+ Returns the number of bytes (of \a dgram) written, or -1 in case of error. This function should
+ set the error code and description if some error was encountered.
+
+ \sa decryptDatagram()
+*/
+
+/*!
+ \fn QByteArray DtlsCryptograph::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram)
+ \internal
+
+ If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, decrypts \a dgram.
+ The use of \a socket is implementation-specific. This function should return an empty byte array
+ and set the error code and description if some error was encountered.
+*/
+
+#endif // QT_CONFIG(dtls)
+#endif // QT_CONFIG(ssl)
+
+} // namespace QTlsPrivate
+
+#if QT_CONFIG(ssl)
+/*!
+ \internal
+*/
+Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel()
+{
+ if (auto *backend = QSslSocketPrivate::tlsBackendInUse())
+ backend->forceAutotestSecurityLevel();
+}
+
+#endif // QT_CONFIG(ssl)
+
+QT_END_NAMESPACE
+
+#include "moc_qtlsbackend_p.cpp"
diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h
new file mode 100644
index 0000000000..090531014b
--- /dev/null
+++ b/src/network/ssl/qtlsbackend_p.h
@@ -0,0 +1,402 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTLSBACKEND_P_H
+#define QTLSBACKEND_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include "qsslconfiguration.h"
+#include "qsslerror.h"
+#include "qssl_p.h"
+
+#if QT_CONFIG(dtls)
+#include "qdtls.h"
+#endif
+
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qsslcipher.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QSslPreSharedKeyAuthenticator;
+class QSslSocketPrivate;
+class QHostAddress;
+class QSslContext;
+
+class QSslSocket;
+class QByteArray;
+class QSslCipher;
+class QUdpSocket;
+class QIODevice;
+class QSslError;
+class QSslKey;
+
+namespace QTlsPrivate {
+
+class Q_NETWORK_EXPORT TlsKey {
+public:
+ virtual ~TlsKey();
+
+ using KeyType = QSsl::KeyType;
+ using KeyAlgorithm = QSsl::KeyAlgorithm;
+
+ virtual void decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der,
+ const QByteArray &passPhrase, bool deepClear) = 0;
+ virtual void decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem,
+ const QByteArray &passPhrase, bool deepClear) = 0;
+
+ virtual QByteArray toPem(const QByteArray &passPhrase) const = 0;
+ virtual QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const = 0;
+ virtual QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const = 0;
+
+ virtual void fromHandle(Qt::HANDLE handle, KeyType type) = 0;
+ virtual Qt::HANDLE handle() const = 0;
+
+ virtual bool isNull() const = 0;
+ virtual KeyType type() const = 0;
+ virtual KeyAlgorithm algorithm() const = 0;
+ virtual int length() const = 0;
+
+ virtual void clear(bool deepClear) = 0;
+
+ virtual bool isPkcs8() const = 0;
+
+ virtual QByteArray decrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &passPhrase, const QByteArray &iv) const = 0;
+ virtual QByteArray encrypt(Cipher cipher, const QByteArray &data,
+ const QByteArray &key, const QByteArray &iv) const = 0;
+
+ QByteArray pemHeader() const;
+ QByteArray pemFooter() const;
+};
+
+class Q_NETWORK_EXPORT X509Certificate
+{
+public:
+ virtual ~X509Certificate();
+
+ virtual bool isEqual(const X509Certificate &other) const = 0;
+ virtual bool isNull() const = 0;
+ virtual bool isSelfSigned() const = 0;
+ virtual QByteArray version() const = 0;
+ virtual QByteArray serialNumber() const = 0;
+ virtual QStringList issuerInfo(QSslCertificate::SubjectInfo subject) const = 0;
+ virtual QStringList issuerInfo(const QByteArray &attribute) const = 0;
+ virtual QStringList subjectInfo(QSslCertificate::SubjectInfo subject) const = 0;
+ virtual QStringList subjectInfo(const QByteArray &attribute) const = 0;
+
+ virtual QList<QByteArray> subjectInfoAttributes() const = 0;
+ virtual QList<QByteArray> issuerInfoAttributes() const = 0;
+ virtual QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames() const = 0;
+ virtual QDateTime effectiveDate() const = 0;
+ virtual QDateTime expiryDate() const = 0;
+
+ virtual TlsKey *publicKey() const;
+
+ // Extensions. Plugins do not expose internal representation
+ // and cannot rely on QSslCertificate's internals. Thus,
+ // we provide this information 'in pieces':
+ virtual qsizetype numberOfExtensions() const = 0;
+ virtual QString oidForExtension(qsizetype i) const = 0;
+ virtual QString nameForExtension(qsizetype i) const = 0;
+ virtual QVariant valueForExtension(qsizetype i) const = 0;
+ virtual bool isExtensionCritical(qsizetype i) const = 0;
+ virtual bool isExtensionSupported(qsizetype i) const = 0;
+
+ virtual QByteArray toPem() const = 0;
+ virtual QByteArray toDer() const = 0;
+ virtual QString toText() const = 0;
+
+ virtual Qt::HANDLE handle() const = 0;
+
+ virtual size_t hash(size_t seed) const noexcept = 0;
+};
+
+// TLSTODO: consider making those into virtuals in QTlsBackend. After all, we ask the backend
+// to return those pointers if the functionality is supported, but it's a bit odd to have
+// this level of indirection. They are not parts of the classes above because ...
+// you'd then have to ask backend to create a certificate to ... call those
+// functions on a certificate.
+using X509ChainVerifyPtr = QList<QSslError> (*)(const QList<QSslCertificate> &chain,
+ const QString &hostName);
+using X509PemReaderPtr = QList<QSslCertificate> (*)(const QByteArray &pem, int count);
+using X509DerReaderPtr = X509PemReaderPtr;
+using X509Pkcs12ReaderPtr = bool (*)(QIODevice *device, QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
+
+#if QT_CONFIG(ssl)
+// TLS over TCP. Handshake, encryption/decryption.
+class Q_NETWORK_EXPORT TlsCryptograph : public QObject
+{
+public:
+ virtual ~TlsCryptograph();
+
+ virtual void init(QSslSocket *q, QSslSocketPrivate *d) = 0;
+ virtual void checkSettingSslContext(std::shared_ptr<QSslContext> tlsContext);
+ virtual std::shared_ptr<QSslContext> sslContext() const;
+
+ virtual QList<QSslError> tlsErrors() const = 0;
+
+ virtual void startClientEncryption() = 0;
+ virtual void startServerEncryption() = 0;
+ virtual void continueHandshake() = 0;
+ virtual void enableHandshakeContinuation();
+ virtual void disconnectFromHost() = 0;
+ virtual void disconnected() = 0;
+ virtual void cancelCAFetch();
+ virtual QSslCipher sessionCipher() const = 0;
+ virtual QSsl::SslProtocol sessionProtocol() const = 0;
+
+ virtual void transmit() = 0;
+ virtual bool hasUndecryptedData() const;
+ virtual QList<QOcspResponse> ocsps() const;
+
+ static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
+
+ void setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode,
+ const QString &errorDescription) const;
+};
+#else
+class TlsCryptograph;
+#endif // QT_CONFIG(ssl)
+
+#if QT_CONFIG(dtls)
+
+class Q_NETWORK_EXPORT DtlsBase
+{
+public:
+ virtual ~DtlsBase();
+
+ virtual void setDtlsError(QDtlsError code, const QString &description) = 0;
+
+ virtual QDtlsError error() const = 0;
+ virtual QString errorString() const = 0;
+
+ virtual void clearDtlsError() = 0;
+
+ virtual void setConfiguration(const QSslConfiguration &configuration) = 0;
+ virtual QSslConfiguration configuration() const = 0;
+
+ using GenParams = QDtlsClientVerifier::GeneratorParameters;
+ virtual bool setCookieGeneratorParameters(const GenParams &params) = 0;
+ virtual GenParams cookieGeneratorParameters() const = 0;
+};
+
+// DTLS cookie: generation and verification.
+class Q_NETWORK_EXPORT DtlsCookieVerifier : virtual public DtlsBase
+{
+public:
+ virtual bool verifyClient(QUdpSocket *socket, const QByteArray &dgram,
+ const QHostAddress &address, quint16 port) = 0;
+ virtual QByteArray verifiedHello() const = 0;
+};
+
+// TLS over UDP. Handshake, encryption/decryption.
+class Q_NETWORK_EXPORT DtlsCryptograph : virtual public DtlsBase
+{
+public:
+
+ virtual QSslSocket::SslMode cryptographMode() const = 0;
+ virtual void setPeer(const QHostAddress &addr, quint16 port, const QString &name) = 0;
+ virtual QHostAddress peerAddress() const = 0;
+ virtual quint16 peerPort() const = 0;
+ virtual void setPeerVerificationName(const QString &name) = 0;
+ virtual QString peerVerificationName() const = 0;
+
+ virtual void setDtlsMtuHint(quint16 mtu) = 0;
+ virtual quint16 dtlsMtuHint() const = 0;
+
+ virtual QDtls::HandshakeState state() const = 0;
+ virtual bool isConnectionEncrypted() const = 0;
+
+ virtual bool startHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
+ virtual bool handleTimeout(QUdpSocket *socket) = 0;
+ virtual bool continueHandshake(QUdpSocket *socket, const QByteArray &dgram) = 0;
+ virtual bool resumeHandshake(QUdpSocket *socket) = 0;
+ virtual void abortHandshake(QUdpSocket *socket) = 0;
+ virtual void sendShutdownAlert(QUdpSocket *socket) = 0;
+
+ virtual QList<QSslError> peerVerificationErrors() const = 0;
+ virtual void ignoreVerificationErrors(const QList<QSslError> &errorsToIgnore) = 0;
+
+ virtual QSslCipher dtlsSessionCipher() const = 0;
+ virtual QSsl::SslProtocol dtlsSessionProtocol() const = 0;
+
+ virtual qint64 writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) = 0;
+ virtual QByteArray decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) = 0;
+};
+
+#else
+
+class DtlsCookieVerifier;
+class DtlsCryptograph;
+
+#endif // QT_CONFIG(dtls)
+
+} // namespace QTlsPrivate
+
+// Factory, creating back-end specific implementations of
+// different entities QSslSocket is using.
+class Q_NETWORK_EXPORT QTlsBackend : public QObject
+{
+ Q_OBJECT
+public:
+ QTlsBackend();
+ ~QTlsBackend() override;
+
+ virtual bool isValid() const;
+ virtual long tlsLibraryVersionNumber() const;
+ virtual QString tlsLibraryVersionString() const;
+ virtual long tlsLibraryBuildVersionNumber() const;
+ virtual QString tlsLibraryBuildVersionString() const;
+ virtual void ensureInitialized() const;
+
+ virtual QString backendName() const = 0;
+ virtual QList<QSsl::SslProtocol> supportedProtocols() const = 0;
+ virtual QList<QSsl::SupportedFeature> supportedFeatures() const = 0;
+ virtual QList<QSsl::ImplementedClass> implementedClasses() const = 0;
+
+ // X509 and keys:
+ virtual QTlsPrivate::TlsKey *createKey() const;
+ virtual QTlsPrivate::X509Certificate *createCertificate() const;
+
+ virtual QList<QSslCertificate> systemCaCertificates() const;
+
+ // TLS and DTLS:
+ virtual QTlsPrivate::TlsCryptograph *createTlsCryptograph() const;
+ virtual QTlsPrivate::DtlsCryptograph *createDtlsCryptograph(class QDtls *qObject, int mode) const;
+ virtual QTlsPrivate::DtlsCookieVerifier *createDtlsCookieVerifier() const;
+
+ // TLSTODO - get rid of these function pointers, make them virtuals in
+ // the backend itself. X509 machinery:
+ virtual QTlsPrivate::X509ChainVerifyPtr X509Verifier() const;
+ virtual QTlsPrivate::X509PemReaderPtr X509PemReader() const;
+ virtual QTlsPrivate::X509DerReaderPtr X509DerReader() const;
+ virtual QTlsPrivate::X509Pkcs12ReaderPtr X509Pkcs12Reader() const;
+
+ // Elliptic curves:
+ virtual QList<int> ellipticCurvesIds() const;
+ virtual int curveIdFromShortName(const QString &name) const;
+ virtual int curveIdFromLongName(const QString &name) const;
+ virtual QString shortNameForId(int cid) const;
+ virtual QString longNameForId(int cid) const;
+ virtual bool isTlsNamedCurve(int cid) const;
+
+ // Note: int and not QSslDiffieHellmanParameter::Error - because this class and
+ // its enum are QT_CONFIG(ssl)-conditioned. But not QTlsBackend and
+ // its virtual functions. DH decoding:
+ virtual int dhParametersFromDer(const QByteArray &derData, QByteArray *data) const;
+ virtual int dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const;
+
+ static QList<QString> availableBackendNames();
+ static QString defaultBackendName();
+ static QTlsBackend *findBackend(const QString &backendName);
+ static QTlsBackend *activeOrAnyBackend();
+
+ static QList<QSsl::SslProtocol> supportedProtocols(const QString &backendName);
+ static QList<QSsl::SupportedFeature> supportedFeatures(const QString &backendName);
+ static QList<QSsl::ImplementedClass> implementedClasses(const QString &backendName);
+
+ // Built-in, this is what Qt provides out of the box (depending on OS):
+ static constexpr const int nameIndexSchannel = 0;
+ static constexpr const int nameIndexSecureTransport = 1;
+ static constexpr const int nameIndexOpenSSL = 2;
+ static constexpr const int nameIndexCertOnly = 3;
+
+ static const QString builtinBackendNames[];
+
+ template<class DynamicType, class TLSObject>
+ static DynamicType *backend(const TLSObject &o)
+ {
+ return static_cast<DynamicType *>(o.d->backend.get());
+ }
+
+ static void resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend);
+
+ static void setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint,
+ int hintLength, unsigned maxIdentityLen, unsigned maxPskLen);
+ static void setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity,
+ const QByteArray &identityHint, unsigned maxPskLen);
+#if QT_CONFIG(ssl)
+ static QSslCipher createCiphersuite(const QString &description, int bits, int supportedBits);
+ static QSslCipher createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
+ const QString &protocolString);
+ static QSslCipher createCiphersuite(const QString &name, const QString &keyExchangeMethod,
+ const QString &encryptionMethod,
+ const QString &authenticationMethod,
+ int bits, QSsl::SslProtocol protocol,
+ const QString &protocolString);
+
+ // Those statics are implemented using QSslSocketPrivate (which is not exported,
+ // unlike QTlsBackend).
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> defaultDtlsCiphers();
+
+ static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static void setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers);
+ static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
+
+ static void resetDefaultEllipticCurves();
+
+ static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
+
+ // Many thanks to people who designed QSslConfiguration with hidden
+ // data-members, that sneakily set by some 'friend' classes, having
+ // some twisted logic.
+ static bool rootLoadingOnDemandAllowed(const QSslConfiguration &configuration);
+ static void storePeerCertificate(QSslConfiguration &configuration, const QSslCertificate &peerCert);
+ static void storePeerCertificateChain(QSslConfiguration &configuration,
+ const QList<QSslCertificate> &peerCertificateChain);
+ static void clearPeerCertificates(QSslConfiguration &configuration);
+ // And those are even worse, this is where we don't have the original configuration,
+ // and can have only a copy. So instead we go to d->privateConfiguration.someMember:
+ static void clearPeerCertificates(QSslSocketPrivate *d);
+ static void setPeerSessionShared(QSslSocketPrivate *d, bool shared);
+ static void setSessionAsn1(QSslSocketPrivate *d, const QByteArray &asn1);
+ static void setSessionLifetimeHint(QSslSocketPrivate *d, int hint);
+ using AlpnNegotiationStatus = QSslConfiguration::NextProtocolNegotiationStatus;
+ static void setAlpnStatus(QSslSocketPrivate *d, AlpnNegotiationStatus st);
+ static void setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol);
+ static void storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert);
+ static void storePeerCertificateChain(QSslSocketPrivate *d, const QList<QSslCertificate> &peerChain);
+ static void addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert);// TODO: "addTrusted..."
+ // The next one - is a "very important" feature! Kidding ...
+ static void setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key);
+
+ virtual void forceAutotestSecurityLevel();
+#endif // QT_CONFIG(ssl)
+
+ Q_DISABLE_COPY_MOVE(QTlsBackend)
+};
+
+#define QTlsBackend_iid "org.qt-project.Qt.QTlsBackend"
+Q_DECLARE_INTERFACE(QTlsBackend, QTlsBackend_iid);
+
+QT_END_NAMESPACE
+
+#endif // QTLSBACKEND_P_H
diff --git a/src/network/ssl/qwindowscarootfetcher.cpp b/src/network/ssl/qwindowscarootfetcher.cpp
deleted file mode 100644
index f83f96886c..0000000000
--- a/src/network/ssl/qwindowscarootfetcher.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qwindowscarootfetcher_p.h"
-
-#include <QtCore/QThread>
-#include <QtGlobal>
-
-#ifdef QSSLSOCKET_DEBUG
-#include "qssl_p.h" // for debug categories
-#include <QtCore/QElapsedTimer>
-#endif
-
-#include "qsslsocket_p.h" // Transitively includes Wincrypt.h
-
-QT_BEGIN_NAMESPACE
-
-class QWindowsCaRootFetcherThread : public QThread
-{
-public:
- QWindowsCaRootFetcherThread()
- {
- qRegisterMetaType<QSslCertificate>();
- setObjectName(QStringLiteral("QWindowsCaRootFetcher"));
- start();
- }
- ~QWindowsCaRootFetcherThread()
- {
- quit();
- wait(15500); // worst case, a running request can block for 15 seconds
- }
-};
-
-Q_GLOBAL_STATIC(QWindowsCaRootFetcherThread, windowsCaRootFetcherThread);
-
-QWindowsCaRootFetcher::QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode)
- : cert(certificate), mode(sslMode)
-{
- moveToThread(windowsCaRootFetcherThread());
-}
-
-QWindowsCaRootFetcher::~QWindowsCaRootFetcher()
-{
-}
-
-void QWindowsCaRootFetcher::start()
-{
- QByteArray der = cert.toDer();
- PCCERT_CONTEXT wincert = CertCreateCertificateContext(X509_ASN_ENCODING, (const BYTE *)der.constData(), der.length());
- if (!wincert) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl, "QWindowsCaRootFetcher failed to convert certificate to windows form");
-#endif
- emit finished(cert, QSslCertificate());
- deleteLater();
- return;
- }
-
- CERT_CHAIN_PARA parameters;
- memset(&parameters, 0, sizeof(parameters));
- parameters.cbSize = sizeof(parameters);
- // set key usage constraint
- parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
- parameters.RequestedUsage.Usage.cUsageIdentifier = 1;
- LPSTR oid = (LPSTR)(mode == QSslSocket::SslClientMode ? szOID_PKIX_KP_SERVER_AUTH : szOID_PKIX_KP_CLIENT_AUTH);
- parameters.RequestedUsage.Usage.rgpszUsageIdentifier = &oid;
-
-#ifdef QSSLSOCKET_DEBUG
- QElapsedTimer stopwatch;
- stopwatch.start();
-#endif
- PCCERT_CHAIN_CONTEXT chain;
- BOOL result = CertGetCertificateChain(
- nullptr, //default engine
- wincert,
- nullptr, //current date/time
- nullptr, //default store
- &parameters,
- 0, //default dwFlags
- nullptr, //reserved
- &chain);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QWindowsCaRootFetcher" << stopwatch.elapsed() << "ms to get chain";
-#endif
-
- QSslCertificate trustedRoot;
- if (result) {
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QWindowsCaRootFetcher - examining windows chains";
- if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
- qCDebug(lcSsl) << " - TRUSTED";
- else
- qCDebug(lcSsl) << " - NOT TRUSTED" << chain->TrustStatus.dwErrorStatus;
- if (chain->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)
- qCDebug(lcSsl) << " - SELF SIGNED";
- qCDebug(lcSsl) << "QSslSocketBackendPrivate::fetchCaRootForCert - dumping simple chains";
- for (unsigned int i = 0; i < chain->cChain; i++) {
- if (chain->rgpChain[i]->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR)
- qCDebug(lcSsl) << " - TRUSTED SIMPLE CHAIN" << i;
- else
- qCDebug(lcSsl) << " - UNTRUSTED SIMPLE CHAIN" << i << "reason:" << chain->rgpChain[i]->TrustStatus.dwErrorStatus;
- for (unsigned int j = 0; j < chain->rgpChain[i]->cElement; j++) {
- QSslCertificate foundCert(QByteArray((const char *)chain->rgpChain[i]->rgpElement[j]->pCertContext->pbCertEncoded
- , chain->rgpChain[i]->rgpElement[j]->pCertContext->cbCertEncoded), QSsl::Der);
- qCDebug(lcSsl) << " - " << foundCert;
- }
- }
- qCDebug(lcSsl) << " - and" << chain->cLowerQualityChainContext << "low quality chains"; //expect 0, we haven't asked for them
-#endif
-
- //based on http://msdn.microsoft.com/en-us/library/windows/desktop/aa377182%28v=vs.85%29.aspx
- //about the final chain rgpChain[cChain-1] which must begin with a trusted root to be valid
- if (chain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
- && chain->cChain > 0) {
- const PCERT_SIMPLE_CHAIN finalChain = chain->rgpChain[chain->cChain - 1];
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa377544%28v=vs.85%29.aspx
- // rgpElement[0] is the end certificate chain element. rgpElement[cElement-1] is the self-signed "root" certificate element.
- if (finalChain->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR
- && finalChain->cElement > 0) {
- trustedRoot = QSslCertificate(QByteArray((const char *)finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->pbCertEncoded
- , finalChain->rgpElement[finalChain->cElement - 1]->pCertContext->cbCertEncoded), QSsl::Der);
- }
- }
- CertFreeCertificateChain(chain);
- }
- CertFreeCertificateContext(wincert);
-
- emit finished(cert, trustedRoot);
- deleteLater();
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/ssl/qwindowscarootfetcher_p.h b/src/network/ssl/qwindowscarootfetcher_p.h
deleted file mode 100644
index 181c309388..0000000000
--- a/src/network/ssl/qwindowscarootfetcher_p.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtNetwork module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QWINDOWSCAROOTFETCHER_P_H
-#define QWINDOWSCAROOTFETCHER_P_H
-
-#include <QtCore/QtGlobal>
-#include <QtCore/QObject>
-
-#include "qsslsocket.h"
-#include "qsslcertificate.h"
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-QT_BEGIN_NAMESPACE
-
-class QWindowsCaRootFetcher : public QObject
-{
- Q_OBJECT;
-public:
- QWindowsCaRootFetcher(const QSslCertificate &certificate, QSslSocket::SslMode sslMode);
- ~QWindowsCaRootFetcher();
-public slots:
- void start();
-signals:
- void finished(QSslCertificate brokenChain, QSslCertificate caroot);
-private:
- QSslCertificate cert;
- QSslSocket::SslMode mode;
-};
-
-QT_END_NAMESPACE
-
-#endif // QWINDOWSCAROOTFETCHER_P_H
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
deleted file mode 100644
index 0c8b9ccd40..0000000000
--- a/src/network/ssl/ssl.pri
+++ /dev/null
@@ -1,131 +0,0 @@
-# OpenSSL support; compile in QSslSocket.
-
-HEADERS += ssl/qasn1element_p.h \
- ssl/qssl.h \
- ssl/qssl_p.h \
- ssl/qsslcertificate.h \
- ssl/qsslcertificate_p.h \
- ssl/qsslcertificateextension.h \
- ssl/qsslcertificateextension_p.h
-
-SOURCES += ssl/qasn1element.cpp \
- ssl/qssl.cpp \
- ssl/qsslcertificate.cpp \
- ssl/qsslcertificateextension.cpp
-
-!qtConfig(openssl): SOURCES += ssl/qsslcertificate_qt.cpp
-
-qtConfig(ssl) {
- HEADERS += ssl/qsslconfiguration.h \
- ssl/qsslconfiguration_p.h \
- ssl/qsslcipher.h \
- ssl/qsslcipher_p.h \
- ssl/qssldiffiehellmanparameters.h \
- ssl/qssldiffiehellmanparameters_p.h \
- ssl/qsslellipticcurve.h \
- ssl/qsslerror.h \
- ssl/qsslkey.h \
- ssl/qsslkey_p.h \
- ssl/qsslsocket.h \
- ssl/qsslsocket_p.h \
- ssl/qsslpresharedkeyauthenticator.h \
- ssl/qsslpresharedkeyauthenticator_p.h \
- ssl/qocspresponse.h \
- ssl/qocspresponse_p.h
- SOURCES += ssl/qsslconfiguration.cpp \
- ssl/qsslcipher.cpp \
- ssl/qssldiffiehellmanparameters.cpp \
- ssl/qsslellipticcurve.cpp \
- ssl/qsslkey_p.cpp \
- ssl/qsslerror.cpp \
- ssl/qsslsocket.cpp \
- ssl/qsslpresharedkeyauthenticator.cpp \
- ssl/qocspresponse.cpp
-
- winrt {
- HEADERS += ssl/qsslsocket_winrt_p.h
- SOURCES += ssl/qsslcertificate_winrt.cpp \
- ssl/qssldiffiehellmanparameters_dummy.cpp \
- ssl/qsslkey_qt.cpp \
- ssl/qsslkey_winrt.cpp \
- ssl/qsslsocket_winrt.cpp \
- ssl/qsslellipticcurve_dummy.cpp
- }
-
- qtConfig(schannel) {
- HEADERS += ssl/qsslsocket_schannel_p.h
- SOURCES += ssl/qsslsocket_schannel.cpp \
- ssl/qsslcertificate_schannel.cpp \
- ssl/qsslkey_schannel.cpp \
- ssl/qsslkey_qt.cpp \
- ssl/qssldiffiehellmanparameters_dummy.cpp \
- ssl/qsslellipticcurve_dummy.cpp \
- ssl/qsslsocket_qt.cpp
-
- LIBS_PRIVATE += "-lSecur32" "-lCrypt32" "-lbcrypt" "-lncrypt"
- }
-
- qtConfig(securetransport) {
- HEADERS += ssl/qsslsocket_mac_p.h
- SOURCES += ssl/qssldiffiehellmanparameters_dummy.cpp \
- ssl/qsslkey_qt.cpp \
- ssl/qsslkey_mac.cpp \
- ssl/qsslsocket_mac_shared.cpp \
- ssl/qsslsocket_mac.cpp \
- ssl/qsslsocket_qt.cpp \
- ssl/qsslellipticcurve_dummy.cpp
- }
-
- qtConfig(dtls) {
- HEADERS += ssl/qdtls.h \
- ssl/qdtls_p.h
-
- SOURCES += ssl/qdtls.cpp
- }
-
- qtConfig(openssl) {
- HEADERS += ssl/qsslcontext_openssl_p.h \
- ssl/qsslsocket_openssl_p.h \
- ssl/qsslsocket_openssl_symbols_p.h
- SOURCES += ssl/qsslsocket_openssl_symbols.cpp \
- ssl/qssldiffiehellmanparameters_openssl.cpp \
- ssl/qsslcertificate_openssl.cpp \
- ssl/qsslellipticcurve_openssl.cpp \
- ssl/qsslkey_openssl.cpp \
- ssl/qsslsocket_openssl.cpp \
- ssl/qsslcontext_openssl.cpp \
-
- qtConfig(dtls) {
- HEADERS += ssl/qdtls_openssl_p.h
- SOURCES += ssl/qdtls_openssl.cpp
- }
-
- qtConfig(ocsp): HEADERS += ssl/qocsp_p.h
-
- QMAKE_CXXFLAGS += -DOPENSSL_API_COMPAT=0x10100000L
-
- darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp
-
- android:!android-embedded: SOURCES += ssl/qsslsocket_openssl_android.cpp
-
- # Add optional SSL libs
- # Static linking of OpenSSL with msvc:
- # - Binaries http://slproweb.com/products/Win32OpenSSL.html
- # - also needs -lUser32 -lAdvapi32 -lGdi32 -lCrypt32
- # - libs in <OPENSSL_DIR>\lib\VC\static
- # - configure: -openssl -openssl-linked -I <OPENSSL_DIR>\include -L <OPENSSL_DIR>\lib\VC\static OPENSSL_LIBS="-lUser32 -lAdvapi32 -lGdi32" OPENSSL_LIBS_DEBUG="-lssleay32MDd -llibeay32MDd" OPENSSL_LIBS_RELEASE="-lssleay32MD -llibeay32MD"
-
- qtConfig(openssl-linked): \
- QMAKE_USE_FOR_PRIVATE += openssl
- else: \
- QMAKE_USE_FOR_PRIVATE += openssl/nolink
- win32 {
- LIBS_PRIVATE += -lcrypt32
- HEADERS += ssl/qwindowscarootfetcher_p.h
- SOURCES += ssl/qwindowscarootfetcher.cpp
- }
- }
-}
-
-HEADERS += ssl/qpassworddigestor.h
-SOURCES += ssl/qpassworddigestor.cpp