diff options
251 files changed, 10374 insertions, 5442 deletions
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index 0e4e9fe..eb8b630 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in @@ -102,12 +102,6 @@ if(NOT "@CMAKE_GENERATOR@" MATCHES "Xcode") ) endif () -if(NOT "@CMAKE_GENERATOR@" MATCHES "KDevelop") - list(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE - "Kdevelop" - ) -endif () - list(APPEND CTEST_CUSTOM_COVERAGE_EXCLUDE # Exclude kwsys files from coverage results. They are reported # (with better coverage results) on kwsys dashboards... diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst index bdfb3d0..ec76479 100644 --- a/Help/dev/maint.rst +++ b/Help/dev/maint.rst @@ -73,6 +73,13 @@ Merge the ``release-$ver`` branch to ``master``: git pull git merge --no-ff release-$ver +Review new ancestry to ensure nothing unexpected was merged to either branch: + +.. code-block:: shell + + git log --graph --boundary origin/master..master + git log --graph --boundary origin/release..release-$ver + Publish both ``master`` and ``release`` simultaneously: .. code-block:: shell diff --git a/Help/generator/KDevelop3.rst b/Help/generator/KDevelop3.rst deleted file mode 100644 index eaa218b..0000000 --- a/Help/generator/KDevelop3.rst +++ /dev/null @@ -1,25 +0,0 @@ -KDevelop3 ---------- - -Generates KDevelop 3 project files. - -Project files for KDevelop 3 will be created in the top directory and -in every subdirectory which features a CMakeLists.txt file containing -a PROJECT() call. If you change the settings using KDevelop cmake -will try its best to keep your changes when regenerating the project -files. Additionally a hierarchy of UNIX makefiles is generated into -the build tree. Any standard UNIX-style make program can build the -project through the default make target. A "make install" target is -also provided. - -This "extra" generator may be specified as: - -``KDevelop3 - Unix Makefiles`` - Generate with :generator:`Unix Makefiles`. - -``KDevelop3`` - Generate with :generator:`Unix Makefiles`. - - For historical reasons this extra generator may be specified - directly as the main generator and it will be used as the - extra generator with :generator:`Unix Makefiles` automatically. diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst index a37c65b..0287767 100644 --- a/Help/manual/cmake-generators.7.rst +++ b/Help/manual/cmake-generators.7.rst @@ -108,6 +108,5 @@ The following extra generators are known to CMake. /generator/CodeBlocks /generator/CodeLite /generator/Eclipse CDT4 - /generator/KDevelop3 /generator/Kate /generator/Sublime Text 2 diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 88b39d4..00a932f 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -370,10 +370,12 @@ Properties on Source Files /prop_sf/AUTORCC_OPTIONS /prop_sf/COMPILE_DEFINITIONS /prop_sf/COMPILE_FLAGS + /prop_sf/COMPILE_OPTIONS /prop_sf/EXTERNAL_OBJECT /prop_sf/Fortran_FORMAT /prop_sf/GENERATED /prop_sf/HEADER_FILE_ONLY + /prop_sf/INCLUDE_DIRECTORIES /prop_sf/KEEP_EXTENSION /prop_sf/LABELS /prop_sf/LANGUAGE diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 1ddcb1b..0fed0b1 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -277,10 +277,6 @@ Giving the "major" version of the requested protocol version will make the serve use the latest minor version of that protocol. Use this if you do not explicitly need to depend on a specific minor version. -If the build directory already contains a CMake cache, it is sufficient to set -the "buildDirectory" attribute. To create a fresh build directory, additional -attributes are required depending on the protocol version. - Protocol version 1.0 requires the following attributes to be set: * "sourceDirectory" with a path to the sources @@ -290,6 +286,10 @@ Protocol version 1.0 requires the following attributes to be set: * "platform" with the generator platform (if supported by the generator) * "toolset" with the generator toolset (if supported by the generator) +Protocol version 1.2 makes all but the build directory optional, provided +there is a valid cache in the build directory that contains all the other +information already. + Example:: [== "CMake Server" ==[ diff --git a/Help/manual/cpack.1.rst b/Help/manual/cpack.1.rst index 4f9f28d..105bb67 100644 --- a/Help/manual/cpack.1.rst +++ b/Help/manual/cpack.1.rst @@ -62,6 +62,12 @@ Options Run cpack with debug output (for CPack developers). +``--trace`` + Put underlying cmake scripts in trace mode. + +``--trace-expand`` + Put underlying cmake scripts in expanded trace mode. + ``-P <package name>`` override/define CPACK_PACKAGE_NAME diff --git a/Help/prop_sf/COMPILE_OPTIONS.rst b/Help/prop_sf/COMPILE_OPTIONS.rst new file mode 100644 index 0000000..157c0b0 --- /dev/null +++ b/Help/prop_sf/COMPILE_OPTIONS.rst @@ -0,0 +1,17 @@ +COMPILE_OPTIONS +--------------- + +List of additional options to pass to the compiler. + +This property holds a :ref:`;-list <CMake Language Lists>` of options +and will be added to the list of compile flags when this +source file builds. Use :prop_sf:`COMPILE_DEFINITIONS` to pass +additional preprocessor definitions and :prop_sf:`INCLUDE_DIRECTORIES` to pass +additional include directories. + +Contents of ``COMPILE_OPTIONS`` may use "generator expressions" with the +syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. However, :generator:`Xcode` +does not support per-config per-source settings, so expressions +that depend on the build configuration are not allowed with that +generator. diff --git a/Help/prop_sf/INCLUDE_DIRECTORIES.rst b/Help/prop_sf/INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000..55780e5 --- /dev/null +++ b/Help/prop_sf/INCLUDE_DIRECTORIES.rst @@ -0,0 +1,18 @@ +INCLUDE_DIRECTORIES +------------------- + +List of preprocessor include file search directories. + +This property holds a :ref:`;-list <CMake Language Lists>` of paths +and will be added to the list of include directories when this +source file builds. These directories will take precedence over directories +defined at target level except for :generator:`Xcode` generator due to technical +limitations. + +Relative paths should not be added to this property directly. + +Contents of ``INCLUDE_DIRECTORIES`` may use "generator expressions" with +the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. However, :generator:`Xcode` does not support +per-config per-source settings, so expressions that depend on the build +configuration are not allowed with that generator. diff --git a/Help/release/dev/GenerateExportHeader-include-guard.rst b/Help/release/dev/GenerateExportHeader-include-guard.rst new file mode 100644 index 0000000..fa2be42 --- /dev/null +++ b/Help/release/dev/GenerateExportHeader-include-guard.rst @@ -0,0 +1,8 @@ +GenerateExportHeader-include-guard +---------------------------------- + +* The :module:`GenerateExportHeader` module learned an optional + ``INCLUDE_GUARD_NAME`` parameter to change the name of the include guard + symbol written to the generated export header. + Additionally, it now adds a comment after the closing ``#endif`` on the + generated export header's include guard. diff --git a/Help/release/dev/RemoveKDevelop3.rst b/Help/release/dev/RemoveKDevelop3.rst new file mode 100644 index 0000000..805ad26 --- /dev/null +++ b/Help/release/dev/RemoveKDevelop3.rst @@ -0,0 +1,4 @@ +RemoveKDevelop3 +--------------- + +* The ``KDevelop3`` generator has been removed. diff --git a/Help/release/dev/UseJava-add_jar-native-headers.rst b/Help/release/dev/UseJava-add_jar-native-headers.rst new file mode 100644 index 0000000..0370a672 --- /dev/null +++ b/Help/release/dev/UseJava-add_jar-native-headers.rst @@ -0,0 +1,9 @@ +UseJava-add_jar-native-headers +------------------------------ + +* | The command add_jar from :module:`UseJava` module learns how to generate native + headers files using option -h of javac tool. + | This capability requires, at least, version 1.8 of Javac tool. + | Command create_javah will no longer be supported due to the + `suppression of javah tool <http://openjdk.java.net/jeps/313>`_ in the version 1.10 + of the JDK, so ``add_jar(GENERATE_NATIVE_HEADERS)`` must be used instead. diff --git a/Help/release/dev/cpack_trace.rst b/Help/release/dev/cpack_trace.rst new file mode 100644 index 0000000..1152385 --- /dev/null +++ b/Help/release/dev/cpack_trace.rst @@ -0,0 +1,4 @@ +cpack_trace +----------- + +* :manual:`cpack(1)` gained ``--trace`` and ``--trace-expand`` options. diff --git a/Help/release/dev/src-new-properties-COMPILE_OPTIONS-and-INCLUDE_DIRECTORIES.rst b/Help/release/dev/src-new-properties-COMPILE_OPTIONS-and-INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000..d06c109 --- /dev/null +++ b/Help/release/dev/src-new-properties-COMPILE_OPTIONS-and-INCLUDE_DIRECTORIES.rst @@ -0,0 +1,10 @@ +src-new-properties-COMPILE_OPTIONS-and-INCLUDE_DIRECTORIES +---------------------------------------------------------- + +* Source file learns new properties: + + * A :prop_sf:`COMPILE_OPTIONS` source file property was added to manage list + of options to pass to the compiler. + + * An :prop_sf:`INCLUDE_DIRECTORIES` source file property was added to specify + list of preprocessor include file search directories. diff --git a/Help/variable/CMAKE_EXTRA_GENERATOR.rst b/Help/variable/CMAKE_EXTRA_GENERATOR.rst index 4d513e4..2c92323 100644 --- a/Help/variable/CMAKE_EXTRA_GENERATOR.rst +++ b/Help/variable/CMAKE_EXTRA_GENERATOR.rst @@ -4,7 +4,7 @@ CMAKE_EXTRA_GENERATOR The extra generator used to build the project. See :manual:`cmake-generators(7)`. -When using the Eclipse, CodeBlocks or KDevelop generators, CMake +When using the Eclipse, CodeBlocks, CodeLite, Kate or Sublime generators, CMake generates Makefiles (:variable:`CMAKE_GENERATOR`) and additionally project files for the respective IDE. This IDE project file generator is stored in ``CMAKE_EXTRA_GENERATOR`` (e.g. ``Eclipse CDT4``). diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index 31ccef7..9ac9560 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake @@ -158,10 +158,6 @@ if(NOT CMAKE_INCLUDE_FLAG_CXX) set(CMAKE_INCLUDE_FLAG_CXX ${CMAKE_INCLUDE_FLAG_C}) endif() -if(NOT CMAKE_INCLUDE_FLAG_SEP_CXX) - set(CMAKE_INCLUDE_FLAG_SEP_CXX ${CMAKE_INCLUDE_FLAG_SEP_C}) -endif() - # for most systems a module is the same as a shared library # so unless the variable CMAKE_MODULE_EXISTS is set just # copy the values from the LIBRARY variables diff --git a/Modules/CMakeFindKDevelop3.cmake b/Modules/CMakeFindKDevelop3.cmake deleted file mode 100644 index 488acf7..0000000 --- a/Modules/CMakeFindKDevelop3.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - - -# This file is included in CMakeSystemSpecificInformation.cmake if -# the KDevelop3 extra generator has been selected. - -find_program(CMAKE_KDEVELOP3_EXECUTABLE NAMES kdevelop DOC "The KDevelop3 executable") - -if(CMAKE_KDEVELOP3_EXECUTABLE) - set(CMAKE_OPEN_PROJECT_COMMAND "${CMAKE_KDEVELOP3_EXECUTABLE} <PROJECT_FILE>" ) -endif() - diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index 8e5c027..5f028e4 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake @@ -151,10 +151,6 @@ if(NOT CMAKE_INCLUDE_FLAG_Fortran) set(CMAKE_INCLUDE_FLAG_Fortran ${CMAKE_INCLUDE_FLAG_C}) endif() -if(NOT CMAKE_INCLUDE_FLAG_SEP_Fortran) - set(CMAKE_INCLUDE_FLAG_SEP_Fortran ${CMAKE_INCLUDE_FLAG_SEP_C}) -endif() - set(CMAKE_VERBOSE_MAKEFILE FALSE CACHE BOOL "If this value is on, makefiles will be generated without the .SILENT directive, and all commands will be echoed to the console during the make. This is useful for debugging only. With Visual Studio IDE projects all commands are done without /nologo.") set(CMAKE_Fortran_FLAGS_INIT "$ENV{FFLAGS} ${CMAKE_Fortran_FLAGS_INIT}") diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake index 3eb86f9..02cb464 100644 --- a/Modules/CMakeGenericSystem.cmake +++ b/Modules/CMakeGenericSystem.cmake @@ -9,7 +9,6 @@ set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # +s, flag for exe link to use set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "") # -rpath set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP "") # : or empty set(CMAKE_INCLUDE_FLAG_C "-I") # -I -set(CMAKE_INCLUDE_FLAG_C_SEP "") # , or empty set(CMAKE_LIBRARY_PATH_FLAG "-L") set(CMAKE_LIBRARY_PATH_TERMINATOR "") # for the Digital Mars D compiler the link paths have to be terminated with a "/" set(CMAKE_LINK_LIBRARY_FLAG "-l") diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake index de1df3d..35447c9 100644 --- a/Modules/CPackDeb.cmake +++ b/Modules/CPackDeb.cmake @@ -470,7 +470,7 @@ # Usage:: # # set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA -# "${CMAKE_CURRENT_SOURCE_DIR/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm") +# "${CMAKE_CURRENT_SOURCE_DIR}/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm") # # .. note:: # @@ -954,7 +954,7 @@ function(cpack_deb_prepare_package_vars) # - prerm # Usage: # set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA - # "${CMAKE_CURRENT_SOURCE_DIR/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm") + # "${CMAKE_CURRENT_SOURCE_DIR}/prerm;${CMAKE_CURRENT_SOURCE_DIR}/postrm") # Are we packaging components ? if(CPACK_DEB_PACKAGE_COMPONENT) diff --git a/Modules/Compiler/Cray-C.cmake b/Modules/Compiler/Cray-C.cmake index 87ce20f..d34154c 100644 --- a/Modules/Compiler/Cray-C.cmake +++ b/Modules/Compiler/Cray-C.cmake @@ -2,8 +2,7 @@ # file Copyright.txt or https://cmake.org/licensing for details. include(Compiler/Cray) - -set(CMAKE_C_VERBOSE_FLAG "-v") +__compiler_cray(C) string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -DNDEBUG") string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG") diff --git a/Modules/Compiler/Cray-CXX.cmake b/Modules/Compiler/Cray-CXX.cmake index 8506c094..a1899e6 100644 --- a/Modules/Compiler/Cray-CXX.cmake +++ b/Modules/Compiler/Cray-CXX.cmake @@ -2,8 +2,7 @@ # file Copyright.txt or https://cmake.org/licensing for details. include(Compiler/Cray) - -set(CMAKE_CXX_VERBOSE_FLAG "-v") +__compiler_cray(C) string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL_INIT " -DNDEBUG") string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " -DNDEBUG") diff --git a/Modules/Compiler/Cray-Fortran.cmake b/Modules/Compiler/Cray-Fortran.cmake index 5d81bb0..dbf28e3 100644 --- a/Modules/Compiler/Cray-Fortran.cmake +++ b/Modules/Compiler/Cray-Fortran.cmake @@ -1,4 +1,9 @@ -set(CMAKE_Fortran_VERBOSE_FLAG "-v") +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Compiler/Cray) +__compiler_cray(Fortran) + set(CMAKE_Fortran_MODOUT_FLAG -em) set(CMAKE_Fortran_MODDIR_FLAG -J) set(CMAKE_Fortran_MODDIR_DEFAULT .) diff --git a/Modules/Compiler/Cray.cmake b/Modules/Compiler/Cray.cmake index 8fe8eeb..c214afc 100644 --- a/Modules/Compiler/Cray.cmake +++ b/Modules/Compiler/Cray.cmake @@ -8,3 +8,10 @@ endif() set(__COMPILER_CRAY 1) include(Compiler/CMakeCommonCompilerMacros) + +macro(__compiler_cray lang) + set(CMAKE_${lang}_VERBOSE_FLAG "-v") + set(CMAKE_${lang}_COMPILE_OPTIONS_PIC -h PIC) + set(CMAKE_${lang}_COMPILE_OPTIONS_PIE -h PIC) + set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-h PIC") +endmacro() diff --git a/Modules/Compiler/CrayPrgEnv.cmake b/Modules/Compiler/CrayPrgEnv.cmake index 3feea1c..05e3fc7 100644 --- a/Modules/Compiler/CrayPrgEnv.cmake +++ b/Modules/Compiler/CrayPrgEnv.cmake @@ -62,7 +62,6 @@ macro(__CrayPrgEnv_setup lang test_src compiler_cmd link_cmd) # Flags for the Cray wrappers set(CMAKE_STATIC_LIBRARY_LINK_${lang}_FLAGS "-static") - set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-dynamic") diff --git a/Modules/Compiler/XL.cmake b/Modules/Compiler/XL.cmake index 4783785..e527a04 100644 --- a/Modules/Compiler/XL.cmake +++ b/Modules/Compiler/XL.cmake @@ -20,6 +20,8 @@ macro(__compiler_xl lang) # Feature flags. set(CMAKE_${lang}_VERBOSE_FLAG "-V") set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-qpic") + set(CMAKE_${lang}_RESPONSE_FILE_FLAG "-qoptfile=") + set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-qoptfile=") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O") diff --git a/Modules/GenerateExportHeader.cmake b/Modules/GenerateExportHeader.cmake index 4573c2e..17a3357 100644 --- a/Modules/GenerateExportHeader.cmake +++ b/Modules/GenerateExportHeader.cmake @@ -19,6 +19,7 @@ # [EXPORT_FILE_NAME <export_file_name>] # [DEPRECATED_MACRO_NAME <deprecated_macro_name>] # [NO_EXPORT_MACRO_NAME <no_export_macro_name>] +# [INCLUDE_GUARD_NAME <include_guard_name>] # [STATIC_DEFINE <static_define>] # [NO_DEPRECATED_MACRO_NAME <no_deprecated_macro_name>] # [DEFINE_NO_DEPRECATED] @@ -277,7 +278,7 @@ macro(_DO_GENERATE_EXPORT_HEADER TARGET_LIBRARY) set(options DEFINE_NO_DEPRECATED) set(oneValueArgs PREFIX_NAME BASE_NAME EXPORT_MACRO_NAME EXPORT_FILE_NAME DEPRECATED_MACRO_NAME NO_EXPORT_MACRO_NAME STATIC_DEFINE - NO_DEPRECATED_MACRO_NAME CUSTOM_CONTENT_FROM_VARIABLE) + NO_DEPRECATED_MACRO_NAME CUSTOM_CONTENT_FROM_VARIABLE INCLUDE_GUARD_NAME) set(multiValueArgs) cmake_parse_arguments(_GEH "${options}" "${oneValueArgs}" "${multiValueArgs}" @@ -341,7 +342,11 @@ macro(_DO_GENERATE_EXPORT_HEADER TARGET_LIBRARY) endif() string(MAKE_C_IDENTIFIER ${NO_DEPRECATED_MACRO_NAME} NO_DEPRECATED_MACRO_NAME) - set(INCLUDE_GUARD_NAME "${EXPORT_MACRO_NAME}_H") + if(_GEH_INCLUDE_GUARD_NAME) + set(INCLUDE_GUARD_NAME ${_GEH_INCLUDE_GUARD_NAME}) + else() + set(INCLUDE_GUARD_NAME "${EXPORT_MACRO_NAME}_H") + endif() get_target_property(EXPORT_IMPORT_CONDITION ${TARGET_LIBRARY} DEFINE_SYMBOL) diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake index b5fc236..939bd7b 100644 --- a/Modules/UseJava.cmake +++ b/Modules/UseJava.cmake @@ -20,6 +20,7 @@ # [VERSION version] # [OUTPUT_NAME name] # [OUTPUT_DIR dir] +# [GENERATE_NATIVE_HEADERS target [DESTINATION dir]] # ) # # This command creates a <target_name>.jar. It compiles the given @@ -35,6 +36,14 @@ # The default OUTPUT_DIR can also be changed by setting the variable # CMAKE_JAVA_TARGET_OUTPUT_DIR. # +# Optionaly, using option GENERATE_NATIVE_HEADERS, native header files can be generated +# for methods declared as native. These files provide the connective glue that allow your +# Java and C code to interact. An INTERFACE target will be created for an easy usage +# of generated files. Sub-option DESTINATION can be used to specify output directory for +# generated header files. +# +# GENERATE_NATIVE_HEADERS option requires, at least, version 1.8 of the JDK. +# # Additional instructions: # # :: @@ -168,6 +177,22 @@ # # # +# :: +# +# For an optimum usage of option GENERATE_NATIVE_HEADERS, it is recommended to +# include module JNI before any call to add_jar. The produced target for native +# headers can then be used to compile C/C++ sources with command +# target_link_libraries. +# +# +# :: +# +# find_package(JNI) +# add_jar(foo foo.java GENERATE_NATIVE_HEADERS foo-native) +# add_library(bar bar.cpp) +# target_link_libraries(bar PRIVATE foo-native) +# +# # Target Properties: # # :: @@ -359,6 +384,10 @@ # Create C header files from java classes. These files provide the connective glue # that allow your Java and C code to interact. # +# This command will no longer be supported starting with version 1.10 of the JDK due +# to the `suppression of javah tool <http://openjdk.java.net/jeps/313>`_. +# Command ``add_jar(GENERATE_NATIVE_HEADERS)`` must be used instead. +# # There are two main signatures for create_javah. The first signature # returns generated files through variable specified by GENERATED_FILES option: # @@ -448,7 +477,7 @@ function(add_jar _TARGET_NAME) cmake_parse_arguments(_add_jar "" "VERSION;OUTPUT_DIR;OUTPUT_NAME;ENTRY_POINT;MANIFEST" - "SOURCES;INCLUDE_JARS" + "SOURCES;INCLUDE_JARS;GENERATE_NATIVE_HEADERS" ${ARGN} ) @@ -494,6 +523,31 @@ function(add_jar _TARGET_NAME) get_filename_component (_MANIFEST_VALUE "${_add_jar_MANIFEST}" ABSOLUTE) endif () + unset (_GENERATE_NATIVE_HEADERS) + if (_add_jar_GENERATE_NATIVE_HEADERS) + # Raise an error if JDK version is less than 1.8 because javac -h is not supported + # by earlier versions. + if ("${Java_VERSION}" VERSION_LESS 1.8) + message (FATAL_ERROR "ADD_JAR: GENERATE_NATIVE_HEADERS is not supported with this version of Java.") + endif() + cmake_parse_arguments (_add_jar_GENERATE_NATIVE_HEADERS "" "DESTINATION" "" ${_add_jar_GENERATE_NATIVE_HEADERS}) + if (NOT _add_jar_GENERATE_NATIVE_HEADERS_UNPARSED_ARGUMENTS) + message (FATAL_ERROR "ADD_JAR: GENERATE_NATIVE_HEADERS: missing required argument.") + endif() + list (LENGTH _add_jar_GENERATE_NATIVE_HEADERS_UNPARSED_ARGUMENTS length) + if (length GREATER 1) + list (REMOVE_AT _add_jar_GENERATE_NATIVE_HEADERS_UNPARSED_ARGUMENTS 0) + message (FATAL_ERROR "ADD_JAR: GENERATE_NATIVE_HEADERS: ${_add_jar_GENERATE_NATIVE_HEADERS_UNPARSED_ARGUMENTS}: unexpected argument(s).") + endif() + if (NOT _add_jar_GENERATE_NATIVE_HEADERS_DESTINATION) + set (_add_jar_GENERATE_NATIVE_HEADERS_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir/native_headers") + endif() + + set (_GENERATE_NATIVE_HEADERS_TARGET ${_add_jar_GENERATE_NATIVE_HEADERS_UNPARSED_ARGUMENTS}) + set (_GENERATE_NATIVE_HEADERS_OUTPUT_DIR "${_add_jar_GENERATE_NATIVE_HEADERS_DESTINATION}") + set (_GENERATE_NATIVE_HEADERS -h "${_GENERATE_NATIVE_HEADERS_OUTPUT_DIR}") + endif() + if (LIBRARY_OUTPUT_PATH) set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}) else () @@ -625,6 +679,7 @@ function(add_jar _TARGET_NAME) ${CMAKE_JAVA_COMPILE_FLAGS} -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}" -d ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + ${_GENERATE_NATIVE_HEADERS} ${_JAVA_SOURCES_FILELISTS} COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} DEPENDS ${_JAVA_COMPILE_FILES} ${_JAVA_COMPILE_FILELISTS} ${_JAVA_COMPILE_DEPENDS} @@ -735,6 +790,17 @@ function(add_jar _TARGET_NAME) ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ) + if (_GENERATE_NATIVE_HEADERS) + # create an INTERFACE library encapsulating include directory for generated headers + add_library (${_GENERATE_NATIVE_HEADERS_TARGET} INTERFACE) + target_include_directories (${_GENERATE_NATIVE_HEADERS_TARGET} INTERFACE + "${_GENERATE_NATIVE_HEADERS_OUTPUT_DIR}" + ${JNI_INCLUDE_DIRS}) + # this INTERFACE library depends on jar generation + add_dependencies (${_GENERATE_NATIVE_HEADERS_TARGET} ${_TARGET_NAME}) + + set_property (DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_GENERATE_NATIVE_HEADERS_OUTPUT_DIR}") + endif() endfunction() function(INSTALL_JAR _TARGET_NAME) @@ -1246,6 +1312,12 @@ function(create_javadoc _target) endfunction() function (create_javah) + if ("${Java_VERSION}" VERSION_GREATER_EQUAL 1.10) + message (FATAL_ERROR "create_javah: not supported with this Java version. Use add_jar(GENERATE_NATIVE_HEADERS) instead.") + elseif ("${Java_VERSION}" VERSION_GREATER_EQUAL 1.8) + message (DEPRECATION "create_javah: this command will no longer be supported starting with version 1.10 of JDK. Update your project by using command add_jar(GENERATE_NATIVE_HEADERS) instead.") + endif() + cmake_parse_arguments(_create_javah "" "TARGET;GENERATED_FILES;OUTPUT_NAME;OUTPUT_DIR" diff --git a/Modules/exportheader.cmake.in b/Modules/exportheader.cmake.in index 9dd75bf..c518b3d 100644 --- a/Modules/exportheader.cmake.in +++ b/Modules/exportheader.cmake.in @@ -39,4 +39,4 @@ # endif #endif @CUSTOM_CONTENT@ -#endif +#endif /* @INCLUDE_GUARD_NAME@ */ diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index da5dc60..010eeb3 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -610,11 +610,6 @@ set(SRCS SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) -# Kdevelop only works on UNIX and not windows -if(UNIX) - set(SRCS ${SRCS} cmGlobalKdevelopGenerator.cxx) -endif() - # Xcode only works on Apple if(APPLE) set(SRCS ${SRCS} diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 2bd81d1..89c4651 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 10) -set(CMake_VERSION_PATCH 20180123) +set(CMake_VERSION_PATCH 20180126) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index 9590aa2..fabcf60 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -639,6 +639,8 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( cm.GetCurrentSnapshot().SetDefaultDefinitions(); cm.AddCMakePaths(); cm.SetProgressCallback(cmCPackGeneratorProgress, this); + cm.SetTrace(this->Trace); + cm.SetTraceExpand(this->TraceExpand); cmGlobalGenerator gg(&cm); cmMakefile mf(&gg, cm.GetCurrentSnapshot()); if (!installSubDirectory.empty() && installSubDirectory != "/" && diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h index 8100b66..c22f36b 100644 --- a/Source/CPack/cmCPackGenerator.h +++ b/Source/CPack/cmCPackGenerator.h @@ -36,6 +36,16 @@ public: } /** + * Put underlying cmake scripts in trace mode. + */ + void SetTrace(bool val) { this->Trace = val; } + + /** + * Put underlying cmake scripts in expanded trace mode. + */ + void SetTraceExpand(bool val) { this->TraceExpand = val; } + + /** * Returns true if the generator may work on this system. * Rational: * Some CPack generator may run on some host and may not on others @@ -295,6 +305,8 @@ protected: ComponentPackageMethod componentPackageMethod; cmCPackLog* Logger; + bool Trace; + bool TraceExpand; private: cmMakefile* MakefileMap; diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index addb54e..2b2152c 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -43,6 +43,8 @@ static const char* cmDocumentationOptions[][2] = { { "-D <var>=<value>", "Set a CPack variable." }, { "--config <config file>", "Specify the config file." }, { "--verbose,-V", "enable verbose output" }, + { "--trace", "Put underlying cmake scripts in trace mode." }, + { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." }, { "--debug", "enable debug output (for CPack developers)" }, { "-P <package name>", "override/define CPACK_PACKAGE_NAME" }, { "-R <package version>", "override/define CPACK_PACKAGE_VERSION" }, @@ -98,6 +100,8 @@ int main(int argc, char const* const* argv) argc = args.argc(); argv = args.argv(); + cmSystemTools::EnableMSVCDebugHook(); + cmSystemTools::InitializeLibUV(); cmSystemTools::FindCMakeResources(argv[0]); cmCPackLog log; @@ -106,8 +110,6 @@ int main(int argc, char const* const* argv) log.SetOutputPrefix("CPack: "); log.SetVerbosePrefix("CPack Verbose: "); - cmSystemTools::EnableMSVCDebugHook(); - if (cmSystemTools::GetCurrentWorkingDirectory().empty()) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Current working directory cannot be established." @@ -119,6 +121,8 @@ int main(int argc, char const* const* argv) bool help = false; bool helpVersion = false; bool verbose = false; + bool trace = false; + bool traceExpand = false; bool debug = false; std::string helpFull; std::string helpMAN; @@ -154,6 +158,10 @@ int main(int argc, char const* const* argv) arg.AddArgument("--debug", argT::NO_ARGUMENT, &debug, "-V"); arg.AddArgument("--config", argT::SPACE_ARGUMENT, &cpackConfigFile, "CPack configuration file"); + arg.AddArgument("--trace", argT::NO_ARGUMENT, &trace, + "Put underlying cmake scripts in trace mode."); + arg.AddArgument("--trace-expand", argT::NO_ARGUMENT, &traceExpand, + "Put underlying cmake scripts in expanded trace mode."); arg.AddArgument("-C", argT::SPACE_ARGUMENT, &cpackBuildConfig, "CPack build configuration"); arg.AddArgument("-G", argT::SPACE_ARGUMENT, &generator, "CPack generator"); @@ -197,6 +205,14 @@ int main(int argc, char const* const* argv) globalMF.AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0"); #endif + if (trace) { + cminst.SetTrace(true); + } + if (traceExpand) { + cminst.SetTrace(true); + cminst.SetTraceExpand(true); + } + bool cpackConfigFileSpecified = true; if (cpackConfigFile.empty()) { cpackConfigFile = cmSystemTools::GetCurrentWorkingDirectory(); @@ -340,6 +356,10 @@ int main(int argc, char const* const* argv) << std::endl); parsed = 0; } + + cpackGenerator->SetTrace(trace); + cpackGenerator->SetTraceExpand(traceExpand); + if (parsed && !cpackGenerator->Initialize(gen, mf)) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Cannot initialize the generator " << gen diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx index 97d5579..17cf628 100644 --- a/Source/CursesDialog/ccmake.cxx +++ b/Source/CursesDialog/ccmake.cxx @@ -75,6 +75,7 @@ int main(int argc, char const* const* argv) argc = encoding_args.argc(); argv = encoding_args.argv(); + cmSystemTools::InitializeLibUV(); cmSystemTools::FindCMakeResources(argv[0]); cmDocumentation doc; doc.addCMakeStandardDocSections(); diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index bfd43cf..193f4d3 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -55,6 +55,7 @@ int main(int argc, char** argv) int argc2 = encoding_args.argc(); char const* const* argv2 = encoding_args.argv(); + cmSystemTools::InitializeLibUV(); cmSystemTools::FindCMakeResources(argv2[0]); // check docs first so that X is not need to get docs // do docs, if args were given diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 7e94a27..a84429b 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -39,15 +39,6 @@ QCMake::QCMake(QObject* p) std::vector<cmake::GeneratorInfo>::const_iterator it; for (it = generators.begin(); it != generators.end(); ++it) { - // Skip the generator "KDevelop3", since there is also - // "KDevelop3 - Unix Makefiles", which is the full and official name. - // The short name is actually only still there since this was the name - // in CMake 2.4, to keep "command line argument compatibility", but - // this is not necessary in the GUI. - if (it->name == "KDevelop3") { - continue; - } - this->AvailableGenerators.push_back(*it); } } diff --git a/Source/cmAddExecutableCommand.cxx b/Source/cmAddExecutableCommand.cxx index b9e200a..2e95032 100644 --- a/Source/cmAddExecutableCommand.cxx +++ b/Source/cmAddExecutableCommand.cxx @@ -164,7 +164,7 @@ bool cmAddExecutableCommand::InitialPass(std::vector<std::string> const& args, std::vector<std::string> srclists(s, args.end()); cmTarget* tgt = - this->Makefile->AddExecutable(exename.c_str(), srclists, excludeFromAll); + this->Makefile->AddExecutable(exename, srclists, excludeFromAll); if (use_win32) { tgt->SetProperty("WIN32_EXECUTABLE", "ON"); } diff --git a/Source/cmExternalMakefileProjectGenerator.h b/Source/cmExternalMakefileProjectGenerator.h index 7f332a8..5cc6442 100644 --- a/Source/cmExternalMakefileProjectGenerator.h +++ b/Source/cmExternalMakefileProjectGenerator.h @@ -17,7 +17,7 @@ class cmMakefile; * cmExternalMakefileProjectGenerator is a base class for generators * for "external makefile based projects", i.e. IDE projects which work * an already existing makefiles. - * See cmGlobalKdevelopGenerator as an example. + * See cmExtraEclipseCDT4Generator as an example. * After the makefiles have been generated by one of the Makefile * generators, the Generate() method is called and this generator * can iterate over the local generators and/or projects to produce the diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index 31c8bca..e4bec7f 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -416,7 +416,7 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( // C/C++ source files, // replacing the file name extension with ".h" and checks whether such a // file exists. If it does, it is inserted into the map of files. - // A very similar version of that code exists also in the kdevelop + // A very similar version of that code exists also in the CodeLite // project generator. for (std::string const& fileName : cFiles) { std::string headerBasename = cmSystemTools::GetFilenamePath(fileName); diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index fad0723..a37348d 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -300,7 +300,7 @@ void cmExtraCodeLiteGenerator::FindMatchingHeaderfiles( // files to the project. It does that by iterating over all source files, // replacing the file name extension with ".h" and checks whether such a // file exists. If it does, it is inserted into the map of files. - // A very similar version of that code exists also in the kdevelop + // A very similar version of that code exists also in the CodeBlocks // project generator. for (auto const& sit : cFiles) { std::string headerBasename = cmSystemTools::GetFilenamePath(sit.first); diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index a200385..1fc0828 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -498,7 +498,7 @@ void cmExtraEclipseCDT4Generator::CreateLinksForTargets(cmXMLWriter& xml) // Add the file to the list of sources. std::string const& source = sf->GetFullPath(); cmSourceGroup* sourceGroup = - makefile->FindSourceGroup(source.c_str(), sourceGroups); + makefile->FindSourceGroup(source, sourceGroups); sourceGroup->AssignSource(sf); } diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx index c7197f2..a89c187 100644 --- a/Source/cmExtraSublimeTextGenerator.cxx +++ b/Source/cmExtraSublimeTextGenerator.cxx @@ -257,6 +257,8 @@ void cmExtraSublimeTextGenerator::AppendTarget( std::string flagsString = this->ComputeFlagsForObject(sourceFile, lg, target); std::string definesString = this->ComputeDefines(sourceFile, lg, target); + std::string includesString = + this->ComputeIncludes(sourceFile, lg, target); flags.clear(); cmsys::RegularExpression flagRegex; // Regular expression to extract compiler flags from a string @@ -264,7 +266,8 @@ void cmExtraSublimeTextGenerator::AppendTarget( const char* regexString = "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?"; flagRegex.compile(regexString); - std::string workString = flagsString + " " + definesString; + std::string workString = + flagsString + " " + definesString + " " + includesString; while (flagRegex.find(workString)) { std::string::size_type start = flagRegex.start(); if (workString[start] == ' ') { @@ -351,23 +354,21 @@ std::string cmExtraSublimeTextGenerator::ComputeFlagsForObject( lg->GetTargetCompileFlags(gtgt, config, language, flags); - // Add include directory flags. - { - std::vector<std::string> includes; - lg->GetIncludeDirectories(includes, gtgt, language, config); - std::string includeFlags = lg->GetIncludeFlags(includes, gtgt, language, - true); // full include paths - lg->AppendFlags(flags, includeFlags); - } - // Add source file specific flags. + cmGeneratorExpressionInterpreter genexInterpreter(lg, gtgt, config, + gtgt->GetName(), language); + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); if (const char* cflags = source->GetProperty(COMPILE_FLAGS)) { - cmGeneratorExpressionInterpreter genexInterpreter( - lg, gtgt, config, gtgt->GetName(), language); lg->AppendFlags(flags, genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = source->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + } + return flags; } @@ -410,6 +411,34 @@ std::string cmExtraSublimeTextGenerator::ComputeDefines( return definesString; } +std::string cmExtraSublimeTextGenerator::ComputeIncludes( + cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* target) + +{ + std::vector<std::string> includes; + cmMakefile* makefile = lg->GetMakefile(); + const std::string& language = source->GetLanguage(); + const std::string& config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); + cmGeneratorExpressionInterpreter genexInterpreter( + lg, target, config, target->GetName(), language); + + // Add include directories for this source file + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) { + lg->AppendIncludeDirectories( + includes, genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES), + *source); + } + + // Add include directory flags. + lg->GetIncludeDirectories(includes, target, language, config); + + std::string includesString = + lg->GetIncludeFlags(includes, target, language, true, false, config); + + return includesString; +} + bool cmExtraSublimeTextGenerator::Open(const std::string& bindir, const std::string& projectName, bool dryRun) diff --git a/Source/cmExtraSublimeTextGenerator.h b/Source/cmExtraSublimeTextGenerator.h index 57ba1cf..bc158f6 100644 --- a/Source/cmExtraSublimeTextGenerator.h +++ b/Source/cmExtraSublimeTextGenerator.h @@ -65,6 +65,9 @@ private: std::string ComputeDefines(cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* gtgt); + std::string ComputeIncludes(cmSourceFile* source, cmLocalGenerator* lg, + cmGeneratorTarget* gtgt); + bool Open(const std::string& bindir, const std::string& projectName, bool dryRun) override; diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index b3e3393..d459436 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -486,7 +486,7 @@ void cmGhsMultiTargetGenerator::WriteSources( for (std::vector<cmSourceFile*>::const_iterator si = objectSources.begin(); si != objectSources.end(); ++si) { std::vector<cmSourceGroup> sourceGroups(this->Makefile->GetSourceGroups()); - char const* sourceFullPath = (*si)->GetFullPath().c_str(); + std::string const& sourceFullPath = (*si)->GetFullPath(); cmSourceGroup* sourceGroup = this->Makefile->FindSourceGroup(sourceFullPath, sourceGroups); std::string sgPath = sourceGroup->GetFullName(); @@ -604,7 +604,7 @@ std::string cmGhsMultiTargetGenerator::ComputeLongestObjectDirectory( dir_max += "/"; std::vector<cmSourceGroup> sourceGroups( localGhsMultiGenerator->GetMakefile()->GetSourceGroups()); - char const* const sourceFullPath = sourceFile->GetFullPath().c_str(); + std::string const& sourceFullPath = sourceFile->GetFullPath(); cmSourceGroup* sourceGroup = localGhsMultiGenerator->GetMakefile()->FindSourceGroup(sourceFullPath, sourceGroups); diff --git a/Source/cmGlobalKdevelopGenerator.cxx b/Source/cmGlobalKdevelopGenerator.cxx deleted file mode 100644 index 80aadb9..0000000 --- a/Source/cmGlobalKdevelopGenerator.cxx +++ /dev/null @@ -1,589 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmGlobalKdevelopGenerator.h" - -#include "cmGeneratedFileStream.h" -#include "cmGeneratorTarget.h" -#include "cmGlobalGenerator.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmSourceFile.h" -#include "cmStateTypes.h" -#include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmXMLWriter.h" -#include "cmake.h" - -#include "cmsys/Directory.hxx" -#include "cmsys/FStream.hxx" -#include <set> -#include <string.h> -#include <utility> - -cmGlobalKdevelopGenerator::cmGlobalKdevelopGenerator() - : cmExternalMakefileProjectGenerator() -{ -} - -cmExternalMakefileProjectGeneratorFactory* -cmGlobalKdevelopGenerator::GetFactory() -{ - static cmExternalMakefileProjectGeneratorSimpleFactory< - cmGlobalKdevelopGenerator> - factory("KDevelop3", "Generates KDevelop 3 project files."); - - if (factory.GetSupportedGlobalGenerators().empty()) { - factory.AddSupportedGlobalGenerator("Unix Makefiles"); -#ifdef CMAKE_USE_NINJA - factory.AddSupportedGlobalGenerator("Ninja"); -#endif - - factory.Aliases.push_back("KDevelop3"); - } - - return &factory; -} - -void cmGlobalKdevelopGenerator::Generate() -{ - // for each sub project in the project create - // a kdevelop project - for (auto const& it : this->GlobalGenerator->GetProjectMap()) { - std::string outputDir = it.second[0]->GetCurrentBinaryDirectory(); - std::string projectDir = it.second[0]->GetSourceDirectory(); - std::string projectName = it.second[0]->GetProjectName(); - std::string cmakeFilePattern("CMakeLists.txt;*.cmake;"); - std::string fileToOpen; - const std::vector<cmLocalGenerator*>& lgs = it.second; - // create the project.kdevelop.filelist file - if (!this->CreateFilelistFile(lgs, outputDir, projectDir, projectName, - cmakeFilePattern, fileToOpen)) { - cmSystemTools::Error("Can not create filelist file"); - return; - } - // try to find the name of an executable so we have something to - // run from kdevelop for now just pick the first executable found - std::string executable; - for (cmLocalGenerator* lg : lgs) { - std::vector<cmGeneratorTarget*> const& targets = - lg->GetGeneratorTargets(); - for (cmGeneratorTarget* target : targets) { - if (target->GetType() == cmStateEnums::EXECUTABLE) { - executable = target->GetLocation(""); - break; - } - } - if (!executable.empty()) { - break; - } - } - - // now create a project file - this->CreateProjectFile(outputDir, projectDir, projectName, executable, - cmakeFilePattern, fileToOpen); - } -} - -bool cmGlobalKdevelopGenerator::CreateFilelistFile( - const std::vector<cmLocalGenerator*>& lgs, const std::string& outputDir, - const std::string& projectDirIn, const std::string& projectname, - std::string& cmakeFilePattern, std::string& fileToOpen) -{ - std::string projectDir = projectDirIn + "/"; - std::string filename = outputDir + "/" + projectname + ".kdevelop.filelist"; - - std::set<std::string> files; - std::string tmp; - - std::vector<std::string> const& hdrExts = - this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); - - for (cmLocalGenerator* lg : lgs) { - cmMakefile* makefile = lg->GetMakefile(); - const std::vector<std::string>& listFiles = makefile->GetListFiles(); - for (std::string const& listFile : listFiles) { - tmp = listFile; - cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - // make sure the file is part of this source tree - if ((tmp[0] != '/') && - (strstr(tmp.c_str(), cmake::GetCMakeFilesDirectoryPostSlash()) == - nullptr)) { - files.insert(tmp); - tmp = cmSystemTools::GetFilenameName(tmp); - // add all files which don't match the default - // */CMakeLists.txt;*cmake; to the file pattern - if ((tmp != "CMakeLists.txt") && - (strstr(tmp.c_str(), ".cmake") == nullptr)) { - cmakeFilePattern += tmp + ";"; - } - } - } - - // get all sources - const std::vector<cmGeneratorTarget*>& targets = lg->GetGeneratorTargets(); - for (cmGeneratorTarget* gt : targets) { - std::vector<cmSourceFile*> sources; - gt->GetSourceFiles(sources, gt->Target->GetMakefile()->GetSafeDefinition( - "CMAKE_BUILD_TYPE")); - for (cmSourceFile* sf : sources) { - tmp = sf->GetFullPath(); - std::string headerBasename = cmSystemTools::GetFilenamePath(tmp); - headerBasename += "/"; - headerBasename += cmSystemTools::GetFilenameWithoutExtension(tmp); - - cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - - if ((tmp[0] != '/') && - (strstr(tmp.c_str(), cmake::GetCMakeFilesDirectoryPostSlash()) == - nullptr) && - (cmSystemTools::GetFilenameExtension(tmp) != ".moc")) { - files.insert(tmp); - - // check if there's a matching header around - for (std::string const& hdrExt : hdrExts) { - std::string hname = headerBasename; - hname += "."; - hname += hdrExt; - if (cmSystemTools::FileExists(hname.c_str())) { - cmSystemTools::ReplaceString(hname, projectDir.c_str(), ""); - files.insert(hname); - break; - } - } - } - } - for (std::string const& listFile : listFiles) { - tmp = listFile; - cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - if ((tmp[0] != '/') && - (strstr(tmp.c_str(), cmake::GetCMakeFilesDirectoryPostSlash()) == - nullptr)) { - files.insert(tmp); - } - } - } - } - - // check if the output file already exists and read it - // insert all files which exist into the set of files - cmsys::ifstream oldFilelist(filename.c_str()); - if (oldFilelist) { - while (cmSystemTools::GetLineFromStream(oldFilelist, tmp)) { - if (tmp[0] == '/') { - continue; - } - std::string completePath = projectDir + tmp; - if (cmSystemTools::FileExists(completePath.c_str())) { - files.insert(tmp); - } - } - oldFilelist.close(); - } - - // now write the new filename - cmGeneratedFileStream fout(filename.c_str()); - if (!fout) { - return false; - } - - fileToOpen = ""; - for (std::string const& file : files) { - // get the full path to the file - tmp = cmSystemTools::CollapseFullPath(file, projectDir.c_str()); - // just select the first source file - if (fileToOpen.empty()) { - std::string ext = cmSystemTools::GetFilenameExtension(tmp); - if ((ext == ".c") || (ext == ".cc") || (ext == ".cpp") || - (ext == ".cxx") || (ext == ".C") || (ext == ".h") || - (ext == ".hpp")) { - fileToOpen = tmp; - } - } - // make it relative to the project dir - cmSystemTools::ReplaceString(tmp, projectDir.c_str(), ""); - // only put relative paths - if (!tmp.empty() && tmp[0] != '/') { - fout << tmp << "\n"; - } - } - return true; -} - -/* create the project file, if it already exists, merge it with the -existing one, otherwise create a new one */ -void cmGlobalKdevelopGenerator::CreateProjectFile( - const std::string& outputDir, const std::string& projectDir, - const std::string& projectname, const std::string& executable, - const std::string& cmakeFilePattern, const std::string& fileToOpen) -{ - this->Blacklist.clear(); - - std::string filename = outputDir + "/"; - filename += projectname + ".kdevelop"; - std::string sessionFilename = outputDir + "/"; - sessionFilename += projectname + ".kdevses"; - - if (cmSystemTools::FileExists(filename.c_str())) { - this->MergeProjectFiles(outputDir, projectDir, filename, executable, - cmakeFilePattern, fileToOpen, sessionFilename); - } else { - // add all subdirectories which are cmake build directories to the - // kdevelop blacklist so they are not monitored for added or removed files - // since this is handled by adding files to the cmake files - cmsys::Directory d; - if (d.Load(projectDir)) { - size_t numf = d.GetNumberOfFiles(); - for (unsigned int i = 0; i < numf; i++) { - std::string nextFile = d.GetFile(i); - if ((nextFile != ".") && (nextFile != "..")) { - std::string tmp = projectDir; - tmp += "/"; - tmp += nextFile; - if (cmSystemTools::FileIsDirectory(tmp)) { - tmp += "/CMakeCache.txt"; - if ((nextFile == "CMakeFiles") || - (cmSystemTools::FileExists(tmp.c_str()))) { - this->Blacklist.push_back(nextFile); - } - } - } - } - } - this->CreateNewProjectFile(outputDir, projectDir, filename, executable, - cmakeFilePattern, fileToOpen, sessionFilename); - } -} - -void cmGlobalKdevelopGenerator::MergeProjectFiles( - const std::string& outputDir, const std::string& projectDir, - const std::string& filename, const std::string& executable, - const std::string& cmakeFilePattern, const std::string& fileToOpen, - const std::string& sessionFilename) -{ - cmsys::ifstream oldProjectFile(filename.c_str()); - if (!oldProjectFile) { - this->CreateNewProjectFile(outputDir, projectDir, filename, executable, - cmakeFilePattern, fileToOpen, sessionFilename); - return; - } - - /* Read the existing project file (line by line), copy all lines - into the new project file, except the ones which can be reliably - set from contents of the CMakeLists.txt */ - std::string tmp; - std::vector<std::string> lines; - while (cmSystemTools::GetLineFromStream(oldProjectFile, tmp)) { - lines.push_back(tmp); - } - oldProjectFile.close(); - - cmGeneratedFileStream fout(filename.c_str()); - if (!fout) { - return; - } - - for (std::string const& l : lines) { - const char* line = l.c_str(); - // skip these tags as they are always replaced - if ((strstr(line, "<projectdirectory>") != nullptr) || - (strstr(line, "<projectmanagement>") != nullptr) || - (strstr(line, "<absoluteprojectpath>") != nullptr) || - (strstr(line, "<filelistdirectory>") != nullptr) || - (strstr(line, "<buildtool>") != nullptr) || - (strstr(line, "<builddir>") != nullptr)) { - continue; - } - - // output the line from the file if it is not one of the above tags - fout << l << "\n"; - // if this is the <general> tag output the stuff that goes in the - // general tag - if (strstr(line, "<general>")) { - fout << " <projectmanagement>KDevCustomProject</projectmanagement>\n"; - fout << " <projectdirectory>" << projectDir - << "</projectdirectory>\n"; // this one is important - fout << " <absoluteprojectpath>true</absoluteprojectpath>\n"; - // and this one - } - // inside kdevcustomproject the <filelistdirectory> must be put - if (strstr(line, "<kdevcustomproject>")) { - fout << " <filelistdirectory>" << outputDir - << "</filelistdirectory>\n"; - } - // buildtool and builddir go inside <build> - if (strstr(line, "<build>")) { - fout << " <buildtool>make</buildtool>\n"; - fout << " <builddir>" << outputDir << "</builddir>\n"; - } - } -} - -void cmGlobalKdevelopGenerator::CreateNewProjectFile( - const std::string& outputDir, const std::string& projectDir, - const std::string& filename, const std::string& executable, - const std::string& cmakeFilePattern, const std::string& fileToOpen, - const std::string& sessionFilename) -{ - cmGeneratedFileStream fout(filename.c_str()); - if (!fout) { - return; - } - cmXMLWriter xml(fout); - - // check for a version control system - bool hasSvn = cmSystemTools::FileExists((projectDir + "/.svn").c_str()); - bool hasCvs = cmSystemTools::FileExists((projectDir + "/CVS").c_str()); - - bool enableCxx = (this->GlobalGenerator->GetLanguageEnabled("C") || - this->GlobalGenerator->GetLanguageEnabled("CXX")); - bool enableFortran = this->GlobalGenerator->GetLanguageEnabled("Fortran"); - std::string primaryLanguage = "C++"; - if (enableFortran && !enableCxx) { - primaryLanguage = "Fortran77"; - } - - xml.StartDocument(); - xml.StartElement("kdevelop"); - xml.StartElement("general"); - - xml.Element("author", ""); - xml.Element("email", ""); - xml.Element("version", "$VERSION$"); - xml.Element("projectmanagement", "KDevCustomProject"); - xml.Element("primarylanguage", primaryLanguage); - xml.Element("ignoreparts"); - xml.Element("projectdirectory", projectDir); // this one is important - xml.Element("absoluteprojectpath", "true"); // and this one - - // setup additional languages - xml.StartElement("secondaryLanguages"); - if (enableFortran && enableCxx) { - xml.Element("language", "Fortran"); - } - if (enableCxx) { - xml.Element("language", "C"); - } - xml.EndElement(); - - if (hasSvn) { - xml.Element("versioncontrol", "kdevsubversion"); - } else if (hasCvs) { - xml.Element("versioncontrol", "kdevcvsservice"); - } - - xml.EndElement(); // general - xml.StartElement("kdevcustomproject"); - - xml.Element("filelistdirectory", outputDir); - - xml.StartElement("run"); - xml.Element("mainprogram", executable); - xml.Element("directoryradio", "custom"); - xml.Element("customdirectory", outputDir); - xml.Element("programargs", ""); - xml.Element("terminal", "false"); - xml.Element("autocompile", "true"); - xml.Element("envvars"); - xml.EndElement(); - - xml.StartElement("build"); - xml.Element("buildtool", "make"); // this one is important - xml.Element("builddir", outputDir); // and this one - xml.EndElement(); - - xml.StartElement("make"); - xml.Element("abortonerror", "false"); - xml.Element("numberofjobs", 1); - xml.Element("dontact", "false"); - xml.Element("makebin", this->GlobalGenerator->GetLocalGenerators()[0] - ->GetMakefile() - ->GetRequiredDefinition("CMAKE_MAKE_PROGRAM")); - xml.Element("selectedenvironment", "default"); - - xml.StartElement("environments"); - xml.StartElement("default"); - - xml.StartElement("envvar"); - xml.Attribute("value", 1); - xml.Attribute("name", "VERBOSE"); - xml.EndElement(); - - xml.StartElement("envvar"); - xml.Attribute("value", 1); - xml.Attribute("name", "CMAKE_NO_VERBOSE"); - xml.EndElement(); - - xml.EndElement(); // default - xml.EndElement(); // environments - xml.EndElement(); // make - - xml.StartElement("blacklist"); - for (std::string const& dir : this->Blacklist) { - xml.Element("path", dir); - } - xml.EndElement(); - - xml.EndElement(); // kdevcustomproject - - xml.StartElement("kdevfilecreate"); - xml.Element("filetypes"); - xml.StartElement("useglobaltypes"); - - xml.StartElement("type"); - xml.Attribute("ext", "ui"); - xml.EndElement(); - - xml.StartElement("type"); - xml.Attribute("ext", "cpp"); - xml.EndElement(); - - xml.StartElement("type"); - xml.Attribute("ext", "h"); - xml.EndElement(); - - xml.EndElement(); // useglobaltypes - xml.EndElement(); // kdevfilecreate - - xml.StartElement("kdevdoctreeview"); - xml.StartElement("projectdoc"); - xml.Element("userdocDir", "html/"); - xml.Element("apidocDir", "html/"); - xml.EndElement(); // projectdoc - xml.Element("ignoreqt_xml"); - xml.Element("ignoredoxygen"); - xml.Element("ignorekdocs"); - xml.Element("ignoretocs"); - xml.Element("ignoredevhelp"); - xml.EndElement(); // kdevdoctreeview; - - if (enableCxx) { - xml.StartElement("cppsupportpart"); - xml.StartElement("filetemplates"); - xml.Element("interfacesuffix", ".h"); - xml.Element("implementationsuffix", ".cpp"); - xml.EndElement(); // filetemplates - xml.EndElement(); // cppsupportpart - - xml.StartElement("kdevcppsupport"); - xml.StartElement("codecompletion"); - xml.Element("includeGlobalFunctions", "true"); - xml.Element("includeTypes", "true"); - xml.Element("includeEnums", "true"); - xml.Element("includeTypedefs", "false"); - xml.Element("automaticCodeCompletion", "true"); - xml.Element("automaticArgumentsHint", "true"); - xml.Element("automaticHeaderCompletion", "true"); - xml.Element("codeCompletionDelay", 250); - xml.Element("argumentsHintDelay", 400); - xml.Element("headerCompletionDelay", 250); - xml.EndElement(); // codecompletion - xml.Element("references"); - xml.EndElement(); // kdevcppsupport; - } - - if (enableFortran) { - xml.StartElement("kdevfortransupport"); - xml.StartElement("ftnchek"); - xml.Element("division", "false"); - xml.Element("extern", "false"); - xml.Element("declare", "false"); - xml.Element("pure", "false"); - xml.Element("argumentsall", "false"); - xml.Element("commonall", "false"); - xml.Element("truncationall", "false"); - xml.Element("usageall", "false"); - xml.Element("f77all", "false"); - xml.Element("portabilityall", "false"); - xml.Element("argumentsonly"); - xml.Element("commononly"); - xml.Element("truncationonly"); - xml.Element("usageonly"); - xml.Element("f77only"); - xml.Element("portabilityonly"); - xml.EndElement(); // ftnchek - xml.EndElement(); // kdevfortransupport; - } - - // set up file groups. maybe this can be used with the CMake SOURCE_GROUP() - // command - xml.StartElement("kdevfileview"); - xml.StartElement("groups"); - - xml.StartElement("group"); - xml.Attribute("pattern", cmakeFilePattern); - xml.Attribute("name", "CMake"); - xml.EndElement(); - - if (enableCxx) { - xml.StartElement("group"); - xml.Attribute("pattern", "*.h;*.hxx;*.hpp"); - xml.Attribute("name", "Header"); - xml.EndElement(); - - xml.StartElement("group"); - xml.Attribute("pattern", "*.c"); - xml.Attribute("name", "C Sources"); - xml.EndElement(); - - xml.StartElement("group"); - xml.Attribute("pattern", "*.cpp;*.C;*.cxx;*.cc"); - xml.Attribute("name", "C++ Sources"); - xml.EndElement(); - } - - if (enableFortran) { - xml.StartElement("group"); - xml.Attribute("pattern", - "*.f;*.F;*.f77;*.F77;*.f90;*.F90;*.for;*.f95;*.F95"); - xml.Attribute("name", "Fortran Sources"); - xml.EndElement(); - } - - xml.StartElement("group"); - xml.Attribute("pattern", "*.ui"); - xml.Attribute("name", "Qt Designer files"); - xml.EndElement(); - - xml.Element("hidenonprojectfiles", "true"); - xml.EndElement(); // groups - - xml.StartElement("tree"); - xml.Element("hidepatterns", "*.o,*.lo,CVS,*~,cmake*"); - xml.Element("hidenonprojectfiles", "true"); - xml.EndElement(); // tree - - xml.EndElement(); // kdevfileview - xml.EndElement(); // kdevelop; - xml.EndDocument(); - - if (sessionFilename.empty()) { - return; - } - - // and a session file, so that kdevelop opens a file if it opens the - // project the first time - cmGeneratedFileStream devses(sessionFilename.c_str()); - if (!devses) { - return; - } - cmXMLWriter sesxml(devses); - sesxml.StartDocument("UTF-8"); - sesxml.Doctype("KDevPrjSession"); - sesxml.StartElement("KDevPrjSession"); - - sesxml.StartElement("DocsAndViews"); - sesxml.Attribute("NumberOfDocuments", 1); - - sesxml.StartElement("Doc0"); - sesxml.Attribute("NumberOfViews", 1); - sesxml.Attribute("URL", "file://" + fileToOpen); - - sesxml.StartElement("View0"); - sesxml.Attribute("line", 0); - sesxml.Attribute("Type", "Source"); - sesxml.EndElement(); // View0 - - sesxml.EndElement(); // Doc0 - sesxml.EndElement(); // DocsAndViews - sesxml.EndElement(); // KDevPrjSession; -} diff --git a/Source/cmGlobalKdevelopGenerator.h b/Source/cmGlobalKdevelopGenerator.h deleted file mode 100644 index d6c43f3..0000000 --- a/Source/cmGlobalKdevelopGenerator.h +++ /dev/null @@ -1,81 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalKdevelopGenerator_h -#define cmGlobalKdevelopGenerator_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include "cmExternalMakefileProjectGenerator.h" - -#include <string> -#include <vector> - -class cmLocalGenerator; - -/** \class cmGlobalKdevelopGenerator - * \brief Write Unix Makefiles accompanied by KDevelop3 project files. - * - * cmGlobalKdevelopGenerator produces a project file for KDevelop 3 (KDevelop - * > 3.1.1). The project is based on the "Custom Makefile based C/C++" - * project of KDevelop. Such a project consists of Unix Makefiles in the - * build directory together with a \<your_project\>.kdevelop project file, - * which contains the project settings and a \<your_project\>.kdevelop.filelist - * file, which lists the source files relative to the kdevelop project - * directory. The kdevelop project directory is the base source directory. - */ -class cmGlobalKdevelopGenerator : public cmExternalMakefileProjectGenerator -{ -public: - cmGlobalKdevelopGenerator(); - - static cmExternalMakefileProjectGeneratorFactory* GetFactory(); - - void Generate() override; - -private: - /*** Create the foo.kdevelop.filelist file, return false if it doesn't - succeed. If the file already exists the contents will be merged. - */ - bool CreateFilelistFile(const std::vector<cmLocalGenerator*>& lgs, - const std::string& outputDir, - const std::string& projectDirIn, - const std::string& projectname, - std::string& cmakeFilePattern, - std::string& fileToOpen); - - /** Create the foo.kdevelop file. This one calls MergeProjectFiles() - if it already exists, otherwise createNewProjectFile() The project - files will be created in \a outputDir (in the build tree), the - kdevelop project dir will be set to \a projectDir (in the source - tree). \a cmakeFilePattern consists of a lists of all cmake - listfiles used by this CMakeLists.txt */ - void CreateProjectFile(const std::string& outputDir, - const std::string& projectDir, - const std::string& projectname, - const std::string& executable, - const std::string& cmakeFilePattern, - const std::string& fileToOpen); - - /*** Reads the old foo.kdevelop line by line and only replaces the - "important" lines - */ - void MergeProjectFiles(const std::string& outputDir, - const std::string& projectDir, - const std::string& filename, - const std::string& executable, - const std::string& cmakeFilePattern, - const std::string& fileToOpen, - const std::string& sessionFilename); - ///! Creates a new foo.kdevelop and a new foo.kdevses file - void CreateNewProjectFile(const std::string& outputDir, - const std::string& projectDir, - const std::string& filename, - const std::string& executable, - const std::string& cmakeFilePattern, - const std::string& fileToOpen, - const std::string& sessionFilename); - - std::vector<std::string> Blacklist; -}; - -#endif diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index bd51f60..9db21d8 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -743,6 +743,11 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile( if (const char* cflags = sf->GetProperty(COMPILE_FLAGS)) { lg->AppendFlags(flags, genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + } // Add per-source definitions. BuildObjectListOrString flagsBuild(this, false); @@ -759,6 +764,16 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile( flags += flagsBuild.GetString(); } + // Add per-source include directories. + std::vector<std::string> includes; + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) { + lg->AppendIncludeDirectories( + includes, genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES), + *sf); + } + lg->AppendFlags(flags, lg->GetIncludeFlags(includes, gtgt, lang, true)); + cmXCodeObject* buildFile = this->CreateXCodeSourceFileFromPath(sf->GetFullPath(), gtgt, lang, sf); @@ -3521,7 +3536,7 @@ void cmGlobalXCodeGenerator::AppendDefines( } void cmGlobalXCodeGenerator::AppendFlag(std::string& flags, - std::string const& flag) + std::string const& flag) const { // Short-circuit for an empty flag. if (flag.empty()) { diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 2269b25..d173f7a 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -104,7 +104,7 @@ public: bool ShouldStripResourcePath(cmMakefile*) const override; bool SetGeneratorToolset(std::string const& ts, cmMakefile* mf) override; - void AppendFlag(std::string& flags, std::string const& flag); + void AppendFlag(std::string& flags, std::string const& flag) const; protected: void AddExtraIDETargets() override; diff --git a/Source/cmIDEOptions.cxx b/Source/cmIDEOptions.cxx index 4b59f44..354b757 100644 --- a/Source/cmIDEOptions.cxx +++ b/Source/cmIDEOptions.cxx @@ -13,6 +13,7 @@ cmIDEOptions::cmIDEOptions() { this->DoingDefine = false; this->AllowDefine = true; + this->DoingInclude = false; this->AllowSlash = false; this->DoingFollowing = 0; for (int i = 0; i < FlagTableCount; ++i) { @@ -33,6 +34,13 @@ void cmIDEOptions::HandleFlag(const char* flag) return; } + // If the last option was -I then this option is the include directory. + if (this->DoingInclude) { + this->DoingInclude = false; + this->Includes.push_back(flag); + return; + } + // If the last option expected a following value, this is it. if (this->DoingFollowing) { this->FlagMapUpdate(this->DoingFollowing, flag); @@ -53,6 +61,17 @@ void cmIDEOptions::HandleFlag(const char* flag) } return; } + // Look for include directory. + if (this->AllowInclude && flag[1] == 'I') { + if (flag[2] == '\0') { + // The next argument will have the include directory. + this->DoingInclude = true; + } else { + // Store this include directory. + this->Includes.push_back(flag + 2); + } + return; + } // Look through the available flag tables. bool flag_handled = false; @@ -155,6 +174,29 @@ std::vector<std::string> const& cmIDEOptions::GetDefines() const return this->Defines; } +void cmIDEOptions::AddInclude(const std::string& include) +{ + this->Includes.push_back(include); +} + +void cmIDEOptions::AddIncludes(const char* includes) +{ + if (includes) { + // Expand the list of includes. + cmSystemTools::ExpandListArgument(includes, this->Includes); + } +} +void cmIDEOptions::AddIncludes(const std::vector<std::string>& includes) +{ + this->Includes.insert(this->Includes.end(), includes.begin(), + includes.end()); +} + +std::vector<std::string> const& cmIDEOptions::GetIncludes() const +{ + return this->Includes; +} + void cmIDEOptions::AddFlag(std::string const& flag, std::string const& value) { this->FlagMap[flag] = value; diff --git a/Source/cmIDEOptions.h b/Source/cmIDEOptions.h index ded92ca..54cb524 100644 --- a/Source/cmIDEOptions.h +++ b/Source/cmIDEOptions.h @@ -20,12 +20,17 @@ public: cmIDEOptions(); virtual ~cmIDEOptions(); - // Store definitions and flags. + // Store definitions, includes and flags. void AddDefine(const std::string& define); void AddDefines(const char* defines); void AddDefines(const std::vector<std::string>& defines); std::vector<std::string> const& GetDefines() const; + void AddInclude(const std::string& includes); + void AddIncludes(const char* includes); + void AddIncludes(const std::vector<std::string>& includes); + std::vector<std::string> const& GetIncludes() const; + void AddFlag(std::string const& flag, std::string const& value); void AddFlag(std::string const& flag, std::vector<std::string> const& value); void AppendFlag(std::string const& flag, std::string const& value); @@ -76,8 +81,13 @@ protected: // Preprocessor definitions. std::vector<std::string> Defines; + // Include directories. + std::vector<std::string> Includes; + bool DoingDefine; bool AllowDefine; + bool DoingInclude; + bool AllowInclude; bool AllowSlash; cmIDEFlagTable const* DoingFollowing; enum diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index c8d94c0..86b16f8 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -16,6 +16,7 @@ #include "cmMakefile.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" +#include "cmSourceFileLocation.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateTypes.h" @@ -37,6 +38,7 @@ #include <sstream> #include <stdio.h> #include <string.h> +#include <unordered_set> #include <utility> #if defined(__HAIKU__) @@ -792,19 +794,14 @@ void cmLocalGenerator::AddCompileOptions(std::string& flags, if (const char* langFlagRegexStr = this->Makefile->GetDefinition(langFlagRegexVar)) { // Filter flags acceptable to this language. - cmsys::RegularExpression r(langFlagRegexStr); std::vector<std::string> opts; if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) { cmSystemTools::ParseWindowsCommandLine(targetFlags, opts); } target->GetCompileOptions(opts, config, lang); - for (std::string const& opt : opts) { - if (r.find(opt.c_str())) { - // (Re-)Escape this flag. COMPILE_FLAGS were already parsed - // as a command line above, and COMPILE_OPTIONS are escaped. - this->AppendFlagEscape(flags, opt); - } - } + // (Re-)Escape these flags. COMPILE_FLAGS were already parsed + // as a command line above, and COMPILE_OPTIONS are escaped. + this->AppendCompileOptions(flags, opts, langFlagRegexStr); } else { // Use all flags. if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) { @@ -813,10 +810,8 @@ void cmLocalGenerator::AddCompileOptions(std::string& flags, } std::vector<std::string> opts; target->GetCompileOptions(opts, config, lang); - for (std::string const& opt : opts) { - // COMPILE_OPTIONS are escaped. - this->AppendFlagEscape(flags, opt); - } + // COMPILE_OPTIONS are escaped. + this->AppendCompileOptions(flags, opts); } for (auto const& it : target->GetMaxLanguageStandards()) { @@ -1876,7 +1871,7 @@ void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, } void cmLocalGenerator::AppendFlags(std::string& flags, - const std::string& newFlags) + const std::string& newFlags) const { if (!newFlags.empty()) { if (!flags.empty()) { @@ -1886,7 +1881,8 @@ void cmLocalGenerator::AppendFlags(std::string& flags, } } -void cmLocalGenerator::AppendFlags(std::string& flags, const char* newFlags) +void cmLocalGenerator::AppendFlags(std::string& flags, + const char* newFlags) const { if (newFlags && *newFlags) { this->AppendFlags(flags, std::string(newFlags)); @@ -1894,7 +1890,7 @@ void cmLocalGenerator::AppendFlags(std::string& flags, const char* newFlags) } void cmLocalGenerator::AppendFlagEscape(std::string& flags, - const std::string& rawFlag) + const std::string& rawFlag) const { this->AppendFlags(flags, this->EscapeForShell(rawFlag)); } @@ -1930,6 +1926,87 @@ void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, } } +void cmLocalGenerator::AppendCompileOptions(std::string& options, + const char* options_list, + const char* regex) const +{ + // Short-circuit if there are no options. + if (!options_list) { + return; + } + + // Expand the list of options. + std::vector<std::string> options_vec; + cmSystemTools::ExpandListArgument(options_list, options_vec); + this->AppendCompileOptions(options, options_vec, regex); +} + +void cmLocalGenerator::AppendCompileOptions( + std::string& options, const std::vector<std::string>& options_vec, + const char* regex) const +{ + if (regex != nullptr) { + // Filter flags upon specified reges. + cmsys::RegularExpression r(regex); + + for (std::string const& opt : options_vec) { + if (r.find(opt.c_str())) { + this->AppendFlagEscape(options, opt); + } + } + } else { + for (std::string const& opt : options_vec) { + this->AppendFlagEscape(options, opt); + } + } +} + +void cmLocalGenerator::AppendIncludeDirectories( + std::vector<std::string>& includes, const char* includes_list, + const cmSourceFile& sourceFile) const +{ + // Short-circuit if there are no includes. + if (!includes_list) { + return; + } + + // Expand the list of includes. + std::vector<std::string> includes_vec; + cmSystemTools::ExpandListArgument(includes_list, includes_vec); + this->AppendIncludeDirectories(includes, includes_vec, sourceFile); +} + +void cmLocalGenerator::AppendIncludeDirectories( + std::vector<std::string>& includes, + const std::vector<std::string>& includes_vec, + const cmSourceFile& sourceFile) const +{ + std::unordered_set<std::string> uniqueIncludes; + + for (const std::string& include : includes_vec) { + if (!cmSystemTools::FileIsFullPath(include.c_str())) { + std::ostringstream e; + e << "Found relative path while evaluating include directories of " + "\"" + << sourceFile.GetLocation().GetName() << "\":\n \"" << include + << "\"\n"; + + this->IssueMessage(cmake::FATAL_ERROR, e.str()); + return; + } + + std::string inc = include; + + if (!cmSystemTools::IsOff(inc.c_str())) { + cmSystemTools::ConvertToUnixSlashes(inc); + } + + if (uniqueIncludes.insert(inc).second) { + includes.push_back(inc); + } + } +} + void cmLocalGenerator::AppendDefines(std::set<std::string>& defines, const char* defines_list) const { diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 4a7d2ca..a66fa6e 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -118,10 +118,11 @@ public: cmGeneratorTarget const* target, const std::string& lang); ///! Append flags to a string. - virtual void AppendFlags(std::string& flags, const std::string& newFlags); - virtual void AppendFlags(std::string& flags, const char* newFlags); + virtual void AppendFlags(std::string& flags, + const std::string& newFlags) const; + virtual void AppendFlags(std::string& flags, const char* newFlags) const; virtual void AppendFlagEscape(std::string& flags, - const std::string& rawFlag); + const std::string& rawFlag) const; void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target, const std::string& config, const std::string& lang); @@ -152,6 +153,23 @@ public: cmGeneratorTarget* FindGeneratorTargetToUse(const std::string& name) const; /** + * Process a list of include directories + */ + void AppendIncludeDirectories(std::vector<std::string>& includes, + const char* includes_list, + const cmSourceFile& sourceFile) const; + void AppendIncludeDirectories(std::vector<std::string>& includes, + std::string const& includes_list, + const cmSourceFile& sourceFile) const + { + this->AppendIncludeDirectories(includes, includes_list.c_str(), + sourceFile); + } + void AppendIncludeDirectories(std::vector<std::string>& includes, + const std::vector<std::string>& includes_vec, + const cmSourceFile& sourceFile) const; + + /** * Encode a list of preprocessor definitions for the compiler * command line. */ @@ -166,6 +184,22 @@ public: const std::vector<std::string>& defines_vec) const; /** + * Encode a list of compile options for the compiler + * command line. + */ + void AppendCompileOptions(std::string& options, const char* options_list, + const char* regex = nullptr) const; + void AppendCompileOptions(std::string& options, + std::string const& options_list, + const char* regex = nullptr) const + { + this->AppendCompileOptions(options, options_list.c_str(), regex); + } + void AppendCompileOptions(std::string& options, + const std::vector<std::string>& options_vec, + const char* regex = nullptr) const; + + /** * Join a set of defines into a definesString with a space separator. */ void JoinDefines(const std::set<std::string>& defines, diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 28890f0..e2d5322 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -830,8 +830,8 @@ std::string cmLocalUnixMakefileGenerator3::GetRelativeTargetDirectory( return dir; } -void cmLocalUnixMakefileGenerator3::AppendFlags(std::string& flags, - const std::string& newFlags) +void cmLocalUnixMakefileGenerator3::AppendFlags( + std::string& flags, const std::string& newFlags) const { if (this->IsWatcomWMake() && !newFlags.empty()) { std::string newf = newFlags; @@ -845,7 +845,7 @@ void cmLocalUnixMakefileGenerator3::AppendFlags(std::string& flags, } void cmLocalUnixMakefileGenerator3::AppendFlags(std::string& flags, - const char* newFlags) + const char* newFlags) const { this->cmLocalGenerator::AppendFlags(flags, newFlags); } diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index b149524..86c5aab 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -88,8 +88,9 @@ public: const std::string& tgt); // append flags to a string - void AppendFlags(std::string& flags, const std::string& newFlags) override; - void AppendFlags(std::string& flags, const char* newFlags) override; + void AppendFlags(std::string& flags, + const std::string& newFlags) const override; + void AppendFlags(std::string& flags, const char* newFlags) const override; // append an echo command enum EchoColor diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 59c20a9..f38cd79 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -45,15 +45,6 @@ public: extern cmVS7FlagTable cmLocalVisualStudio7GeneratorFlagTable[]; -static void cmConvertToWindowsSlash(std::string& s) -{ - std::string::size_type pos = 0; - while ((pos = s.find('/', pos)) != std::string::npos) { - s[pos] = '\\'; - pos++; - } -} - cmLocalVisualStudio7Generator::cmLocalVisualStudio7Generator( cmGlobalGenerator* gg, cmMakefile* mf) : cmLocalVisualStudioGenerator(gg, mf) @@ -704,11 +695,16 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( targetOptions.Parse(flags.c_str()); targetOptions.Parse(defineFlags.c_str()); targetOptions.ParseFinish(); - std::vector<std::string> targetDefines; if (!langForClCompile.empty()) { + std::vector<std::string> targetDefines; target->GetCompileDefinitions(targetDefines, configName, langForClCompile); + targetOptions.AddDefines(targetDefines); + + std::vector<std::string> targetIncludes; + this->GetIncludeDirectories(targetIncludes, target, langForClCompile, + configName); + targetOptions.AddIncludes(targetIncludes); } - targetOptions.AddDefines(targetDefines); targetOptions.SetVerboseMakefile( this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")); @@ -795,27 +791,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( << this->ConvertToXMLOutputPath(modDir.c_str()) << "\\$(ConfigurationName)\"\n"; } - fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - std::vector<std::string> includes_cl; - if (!langForClCompile.empty()) { - this->GetIncludeDirectories(includes_cl, target, langForClCompile, - configName); - } - std::vector<std::string>::iterator i = includes_cl.begin(); - for (; i != includes_cl.end(); ++i) { - // output the include path - std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); - fout << ipath << ";"; - // if this is fortran then output the include with - // a ConfigurationName on the end of it. - if (this->FortranProject) { - ipath = i->c_str(); - ipath += "/$(ConfigurationName)"; - ipath = this->ConvertToXMLOutputPath(ipath.c_str()); - fout << ipath << ";"; - } - } - fout << "\"\n"; + targetOptions.OutputAdditionalIncludeDirectories( + fout, "\t\t\t\t", "\n", + this->FortranProject ? "Fortran" : langForClCompile); targetOptions.OutputFlagMap(fout, "\t\t\t\t"); targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", langForClCompile); @@ -835,20 +813,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( fout << "\t\t\t<Tool\n" "\t\t\t\tName=\"MASM\"\n" - "\t\t\t\tIncludePaths=\"" ; - std::vector<std::string> includes_masm; - this->GetIncludeDirectories(includes_masm, target, "ASM_MASM", - configName); /* clang-format on */ - const char* sep = ""; - for (i = includes_masm.begin(); i != includes_masm.end(); ++i) { - std::string inc = *i; - cmConvertToWindowsSlash(inc); - fout << sep << this->EscapeForXML(inc); - sep = ";"; - } - fout << "\"\n"; + targetOptions.OutputAdditionalIncludeDirectories(fout, "\t\t\t\t", "\n", + "ASM_MASM"); // Use same preprocessor definitions as VCCLCompilerTool. targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", "ASM_MASM"); @@ -868,16 +836,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( if (this->FortranProject) { tool = "VFResourceCompilerTool"; } - fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n" - << "\t\t\t\tAdditionalIncludeDirectories=\""; - std::vector<std::string> includes_rc; - this->GetIncludeDirectories(includes_rc, target, "RC", configName); - for (i = includes_rc.begin(); i != includes_rc.end(); ++i) { - std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); - fout << ipath << ";"; - } + fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; + targetOptions.OutputAdditionalIncludeDirectories(fout, "\n\t\t\t\t", "", + "RC"); // add the -D flags to the RC tool - fout << "\""; targetOptions.OutputPreprocessorDefinitions(fout, "\n\t\t\t\t", "", "RC"); fout << "/>\n"; tool = "VCMIDLTool"; @@ -885,14 +847,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( tool = "VFMIDLTool"; } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; - fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - std::vector<std::string> includes_midl; - this->GetIncludeDirectories(includes_midl, target, "MIDL", configName); - for (i = includes_midl.begin(); i != includes_midl.end(); ++i) { - std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); - fout << ipath << ";"; - } - fout << "\"\n"; + targetOptions.OutputAdditionalIncludeDirectories(fout, "\n\t\t\t\t", "", + "MIDL"); fout << "\t\t\t\tMkTypLibCompatible=\"false\"\n"; if (gg->GetPlatformName() == "x64") { fout << "\t\t\t\tTargetEnvironment=\"3\"\n"; @@ -1410,7 +1366,7 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout, // Add the file to the list of sources. std::string const source = sf->GetFullPath(); cmSourceGroup* sourceGroup = - this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); + this->Makefile->FindSourceGroup(source, sourceGroups); sourceGroup->AssignSource(sf); } @@ -1440,6 +1396,7 @@ struct cmLVS7GFileConfig std::string CompileDefs; std::string CompileDefsConfig; std::string AdditionalDeps; + std::string IncludeDirs; bool ExcludedFromBuild; }; @@ -1494,6 +1451,12 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( fc.CompileFlags = genexInterpreter.Evaluate(cflags, COMPILE_FLAGS); needfc = true; } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = sf.GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + fc.CompileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + needfc = true; + } if (lg->FortranProject) { switch (cmOutputConverter::GetFortranFormat( sf.GetProperty("Fortran_FORMAT"))) { @@ -1522,6 +1485,12 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( needfc = true; } + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincs = sf.GetProperty(INCLUDE_DIRECTORIES)) { + fc.IncludeDirs = genexInterpreter.Evaluate(cincs, INCLUDE_DIRECTORIES); + needfc = true; + } + // Check for extra object-file dependencies. if (const char* deps = sf.GetProperty("OBJECT_DEPENDS")) { std::vector<std::string> depends; @@ -1652,7 +1621,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( this->WriteCustomRule(fout, configs, source.c_str(), *command, fcinfo); } else if (!fcinfo.FileConfigMap.empty()) { const char* aCompilerTool = "VCCLCompilerTool"; - const char* ppLang = "CXX"; + std::string ppLang = "CXX"; if (this->FortranProject) { aCompilerTool = "VFFortranCompilerTool"; } @@ -1699,7 +1668,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( fout << "\t\t\t\t\t<Tool\n" << "\t\t\t\t\tName=\"" << aCompilerTool << "\"\n"; if (!fc.CompileFlags.empty() || !fc.CompileDefs.empty() || - !fc.CompileDefsConfig.empty()) { + !fc.CompileDefsConfig.empty() || !fc.IncludeDirs.empty()) { Options::Tool tool = Options::Compiler; cmVS7FlagTable const* table = cmLocalVisualStudio7GeneratorFlagTable; @@ -1711,7 +1680,14 @@ bool cmLocalVisualStudio7Generator::WriteGroup( fileOptions.Parse(fc.CompileFlags.c_str()); fileOptions.AddDefines(fc.CompileDefs.c_str()); fileOptions.AddDefines(fc.CompileDefsConfig.c_str()); + // validate source level include directories + std::vector<std::string> includes; + this->AppendIncludeDirectories(includes, fc.IncludeDirs, **sf); + fileOptions.AddIncludes(includes); fileOptions.OutputFlagMap(fout, "\t\t\t\t\t"); + fileOptions.OutputAdditionalIncludeDirectories( + fout, "\t\t\t\t\t", "\n", + ppLang == "CXX" && this->FortranProject ? "Fortran" : ppLang); fileOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t\t", "\n", ppLang); } diff --git a/Source/cmLocalXCodeGenerator.cxx b/Source/cmLocalXCodeGenerator.cxx index 457d1fd..92c958d 100644 --- a/Source/cmLocalXCodeGenerator.cxx +++ b/Source/cmLocalXCodeGenerator.cxx @@ -31,10 +31,10 @@ std::string cmLocalXCodeGenerator::GetTargetDirectory( } void cmLocalXCodeGenerator::AppendFlagEscape(std::string& flags, - const std::string& rawFlag) + const std::string& rawFlag) const { - cmGlobalXCodeGenerator* gg = - static_cast<cmGlobalXCodeGenerator*>(this->GlobalGenerator); + const cmGlobalXCodeGenerator* gg = + static_cast<const cmGlobalXCodeGenerator*>(this->GlobalGenerator); gg->AppendFlag(flags, rawFlag); } diff --git a/Source/cmLocalXCodeGenerator.h b/Source/cmLocalXCodeGenerator.h index 8c9596f..5c22dcf 100644 --- a/Source/cmLocalXCodeGenerator.h +++ b/Source/cmLocalXCodeGenerator.h @@ -31,7 +31,7 @@ public: std::string GetTargetDirectory( cmGeneratorTarget const* target) const override; void AppendFlagEscape(std::string& flags, - const std::string& rawFlag) override; + const std::string& rawFlag) const override; void Generate() override; virtual void GenerateInstallRules(); void ComputeObjectFilenames( diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index eeeb54f..c12cd9e 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -664,7 +664,7 @@ struct file_not_persistent bool operator()(const std::string& path) const { return !(path.find("CMakeTmp") == std::string::npos && - cmSystemTools::FileExists(path.c_str())); + cmSystemTools::FileExists(path)); } }; } @@ -755,8 +755,9 @@ void cmMakefile::AddCustomCommandToTarget( return; } + cmTarget& t = ti->second; if (objLibraryCommands == RejectObjectLibraryCommands && - ti->second.GetType() == cmStateEnums::OBJECT_LIBRARY) { + t.GetType() == cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Target \"" << target << "\" is an OBJECT library " @@ -764,7 +765,7 @@ void cmMakefile::AddCustomCommandToTarget( this->IssueMessage(cmake::FATAL_ERROR, e.str()); return; } - if (ti->second.GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (t.GetType() == cmStateEnums::INTERFACE_LIBRARY) { std::ostringstream e; e << "Target \"" << target << "\" is an INTERFACE library " @@ -791,13 +792,13 @@ void cmMakefile::AddCustomCommandToTarget( cc.SetDepfile(depfile); switch (type) { case cmTarget::PRE_BUILD: - ti->second.AddPreBuildCommand(cc); + t.AddPreBuildCommand(cc); break; case cmTarget::PRE_LINK: - ti->second.AddPreLinkCommand(cc); + t.AddPreLinkCommand(cc); break; case cmTarget::POST_BUILD: - ti->second.AddPostBuildCommand(cc); + t.AddPostBuildCommand(cc); break; } } @@ -1180,14 +1181,14 @@ bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove) static cmsys::RegularExpression valid("^[-/]D[A-Za-z_][A-Za-z0-9_]*(=.*)?$"); // Make sure the definition matches. - if (!valid.find(def.c_str())) { + if (!valid.find(def)) { return false; } // Definitions with non-trivial values require a policy check. static cmsys::RegularExpression trivial( "^[-/]D[A-Za-z_][A-Za-z0-9_]*(=[A-Za-z0-9_.]+)?$"); - if (!trivial.find(def.c_str())) { + if (!trivial.find(def)) { // This definition has a non-trivial value. switch (this->GetPolicyStatus(cmPolicies::CMP0005)) { case cmPolicies::WARN: @@ -1409,9 +1410,9 @@ void cmMakefile::Configure() // make sure the CMakeFiles dir is there std::string filesDir = this->StateSnapshot.GetDirectory().GetCurrentBinary(); filesDir += cmake::GetCMakeFilesDirectory(); - cmSystemTools::MakeDirectory(filesDir.c_str()); + cmSystemTools::MakeDirectory(filesDir); - assert(cmSystemTools::FileExists(currentStart.c_str(), true)); + assert(cmSystemTools::FileExists(currentStart, true)); this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart.c_str()); cmListFile listFile; @@ -1572,7 +1573,7 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath, newSnapshot.GetDirectory().SetCurrentSource(srcPath); newSnapshot.GetDirectory().SetCurrentBinary(binPath); - cmSystemTools::MakeDirectory(binPath.c_str()); + cmSystemTools::MakeDirectory(binPath); cmMakefile* subMf = new cmMakefile(this->GlobalGenerator, newSnapshot); this->GetGlobalGenerator()->AddMakefile(subMf); @@ -1876,7 +1877,7 @@ cmTarget* cmMakefile::AddLibrary(const std::string& lname, return target; } -cmTarget* cmMakefile::AddExecutable(const char* exeName, +cmTarget* cmMakefile::AddExecutable(const std::string& exeName, const std::vector<std::string>& srcs, bool excludeFromAll) { @@ -1936,7 +1937,7 @@ cmSourceFile* cmMakefile::GetSourceFileWithOutput( { // If the queried path is not absolute we use the backward compatible // linear-time search for an output with a matching suffix. - if (!cmSystemTools::FileIsFullPath(name.c_str())) { + if (!cmSystemTools::FileIsFullPath(name)) { return this->LinearGetSourceFileWithOutput(name); } // Otherwise we use an efficient lookup map. @@ -2055,7 +2056,7 @@ cmSourceGroup* cmMakefile::GetOrCreateSourceGroup(const std::string& name) * inherited ones. */ cmSourceGroup* cmMakefile::FindSourceGroup( - const char* source, std::vector<cmSourceGroup>& groups) const + const std::string& source, std::vector<cmSourceGroup>& groups) const { // First search for a group that lists the file explicitly. for (std::vector<cmSourceGroup>::reverse_iterator sg = groups.rbegin(); @@ -3232,7 +3233,7 @@ int cmMakefile::TryCompile(const std::string& srcdir, this->IsSourceFileTryCompile = fast; // does the binary directory exist ? If not create it... if (!cmSystemTools::FileIsDirectory(bindir)) { - cmSystemTools::MakeDirectory(bindir.c_str()); + cmSystemTools::MakeDirectory(bindir); } // change to the tests directory and run cmake @@ -3410,7 +3411,7 @@ std::string cmMakefile::GetModulesFile(const char* filename) const cmSystemTools::ConvertToUnixSlashes(itempl); itempl += "/"; itempl += filename; - if (cmSystemTools::FileExists(itempl.c_str())) { + if (cmSystemTools::FileExists(itempl)) { moduleInCMakeModulePath = itempl; break; } @@ -3422,7 +3423,7 @@ std::string cmMakefile::GetModulesFile(const char* filename) const moduleInCMakeRoot += "/Modules/"; moduleInCMakeRoot += filename; cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot); - if (!cmSystemTools::FileExists(moduleInCMakeRoot.c_str())) { + if (!cmSystemTools::FileExists(moduleInCMakeRoot)) { moduleInCMakeRoot.clear(); } @@ -3554,11 +3555,11 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile, this->AddCMakeOutputFile(soutfile); mode_t perm = 0; - cmSystemTools::GetPermissions(sinfile.c_str(), perm); + cmSystemTools::GetPermissions(sinfile, perm); std::string::size_type pos = soutfile.rfind('/'); if (pos != std::string::npos) { std::string path = soutfile.substr(0, pos); - cmSystemTools::MakeDirectory(path.c_str()); + cmSystemTools::MakeDirectory(path); } if (copyonly) { @@ -3618,7 +3619,7 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile, soutfile.c_str())) { res = 0; } else { - cmSystemTools::SetPermissions(soutfile.c_str(), perm); + cmSystemTools::SetPermissions(soutfile, perm); } cmSystemTools::RemoveFile(tempOutputFile); } @@ -3707,7 +3708,7 @@ void cmMakefile::AddCMakeDependFilesFromUser() cmSystemTools::ExpandListArgument(deps_str, deps); } for (std::string const& dep : deps) { - if (cmSystemTools::FileIsFullPath(dep.c_str())) { + if (cmSystemTools::FileIsFullPath(dep)) { this->AddCMakeDependFile(dep); } else { std::string f = this->GetCurrentSourceDirectory(); diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index f06e2ff..2721277 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -180,7 +180,7 @@ public: /** * Add an executable to the build. */ - cmTarget* AddExecutable(const char* exename, + cmTarget* AddExecutable(const std::string& exename, const std::vector<std::string>& srcs, bool excludeFromAll = false); @@ -516,7 +516,7 @@ public: /** * find what source group this source is in */ - cmSourceGroup* FindSourceGroup(const char* source, + cmSourceGroup* FindSourceGroup(const std::string& source, std::vector<cmSourceGroup>& groups) const; #endif diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 002cc0f..5d80e77 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -445,10 +445,35 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( << "\n"; } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = source.GetProperty(COMPILE_OPTIONS)) { + const char* evaluatedOptions = + genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS); + this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions); + *this->FlagFileStream << "# Custom options: " << relativeObj + << "_OPTIONS = " << evaluatedOptions << "\n" + << "\n"; + } + + // Add include directories from source file properties. + std::vector<std::string> includes; + + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) { + const char* evaluatedIncludes = + genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); + this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes, + source); + *this->FlagFileStream << "# Custom include directories: " << relativeObj + << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes + << "\n" + << "\n"; + } + // Add language-specific defines. std::set<std::string> defines; - // Add source-sepcific preprocessor definitions. + // Add source-specific preprocessor definitions. const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); if (const char* compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) { const char* evaluatedDefs = @@ -565,7 +590,10 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( vars.Defines = definesString.c_str(); - std::string const includesString = "$(" + lang + "_INCLUDES)"; + std::string includesString = this->LocalGenerator->GetIncludeFlags( + includes, this->GeneratorTarget, lang, true, false, config); + this->LocalGenerator->AppendFlags(includesString, + "$(" + lang + "_INCLUDES)"); vars.Includes = includesString.c_str(); // At the moment, it is assumed that C, C++, Fortran, and CUDA have both @@ -1625,10 +1653,17 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, } if (useResponseFile) { + std::string const responseFlagVar = + "CMAKE_" + lang + "_RESPONSE_FILE_FLAG"; + std::string responseFlag = + this->Makefile->GetSafeDefinition(responseFlagVar); + if (responseFlag.empty()) { + responseFlag = "@"; + } std::string name = "includes_"; name += lang; name += ".rsp"; - std::string arg = "@" + + std::string arg = std::move(responseFlag) + this->CreateResponseFile(name.c_str(), includeFlags, this->FlagFileDepends[lang]); this->LocalGenerator->AppendFlags(flags, arg); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index f967168..a442463 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -135,16 +135,23 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( } // Add source file specific flags. + cmGeneratorExpressionInterpreter genexInterpreter( + this->LocalGenerator, this->GeneratorTarget, + this->LocalGenerator->GetConfigName(), this->GeneratorTarget->GetName(), + language); + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); if (const char* cflags = source->GetProperty(COMPILE_FLAGS)) { - cmGeneratorExpressionInterpreter genexInterpreter( - this->LocalGenerator, this->GeneratorTarget, - this->LocalGenerator->GetConfigName(), this->GeneratorTarget->GetName(), - language); this->LocalGenerator->AppendFlags( flags, genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = source->GetProperty(COMPILE_OPTIONS)) { + this->LocalGenerator->AppendCompileOptions( + flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + } + return flags; } @@ -204,6 +211,30 @@ std::string cmNinjaTargetGenerator::ComputeDefines(cmSourceFile const* source, return definesString; } +std::string cmNinjaTargetGenerator::ComputeIncludes( + cmSourceFile const* source, const std::string& language) +{ + std::vector<std::string> includes; + const std::string config = this->LocalGenerator->GetConfigName(); + cmGeneratorExpressionInterpreter genexInterpreter( + this->LocalGenerator, this->GeneratorTarget, config, + this->GeneratorTarget->GetName(), language); + + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) { + this->LocalGenerator->AppendIncludeDirectories( + includes, genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES), + *source); + } + + std::string includesString = this->LocalGenerator->GetIncludeFlags( + includes, this->GeneratorTarget, language, true, false, config); + this->LocalGenerator->AppendFlags(includesString, + this->GetIncludes(language)); + + return includesString; +} + cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const { // Static libraries never depend on other targets for linking. @@ -416,14 +447,20 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string flags = "$FLAGS"; std::string rspfile; std::string rspcontent; - std::string responseFlag; bool const lang_supports_response = !(lang == "RC" || lang == "CUDA"); if (lang_supports_response && this->ForceResponseFile()) { + std::string const responseFlagVar = + "CMAKE_" + lang + "_RESPONSE_FILE_FLAG"; + std::string responseFlag = + this->Makefile->GetSafeDefinition(responseFlagVar); + if (responseFlag.empty()) { + responseFlag = "@"; + } rspfile = "$RSP_FILE"; - responseFlag = "@" + rspfile; + responseFlag += rspfile; rspcontent = " $DEFINES $INCLUDES $FLAGS"; - flags = responseFlag; + flags = std::move(responseFlag); vars.Defines = ""; vars.Includes = ""; } @@ -818,7 +855,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaVars vars; vars["FLAGS"] = this->ComputeFlagsForObject(source, language); vars["DEFINES"] = this->ComputeDefines(source, language); - vars["INCLUDES"] = this->GetIncludes(language); + vars["INCLUDES"] = this->ComputeIncludes(source, language); if (!this->NeedDepTypeMSVC(language)) { vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( objectFileName + ".d", cmOutputConverter::SHELL); diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 770a99d..4660a3a 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -82,6 +82,9 @@ protected: std::string ComputeDefines(cmSourceFile const* source, const std::string& language); + std::string ComputeIncludes(cmSourceFile const* source, + const std::string& language); + std::string ConvertToNinjaPath(const std::string& path) const { return this->GetGlobalGenerator()->ConvertToNinjaPath(path); diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 4c0a354..371d536 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -266,8 +266,8 @@ static void setErrorMessage(std::string* errorMessage, const std::string& text) } } -static bool testHomeDirectory(cmState* state, std::string& value, - std::string* errorMessage) +static bool getOrTestHomeDirectory(cmState* state, std::string& value, + std::string* errorMessage) { const std::string cachedValue = std::string(state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY")); @@ -288,9 +288,10 @@ static bool testHomeDirectory(cmState* state, std::string& value, return true; } -static bool testValue(cmState* state, const std::string& key, - std::string& value, const std::string& keyDescription, - std::string* errorMessage) +static bool getOrTestValue(cmState* state, const std::string& key, + std::string& value, + const std::string& keyDescription, + std::string* errorMessage) { const char* entry = state->GetCacheEntryValue(key); const std::string cachedValue = @@ -337,31 +338,31 @@ bool cmServerProtocol1::DoActivate(const cmServerRequest& request, cmState* state = cm->GetState(); // Check generator: - if (!testValue(state, "CMAKE_GENERATOR", generator, "generator", - errorMessage)) { + if (!getOrTestValue(state, "CMAKE_GENERATOR", generator, "generator", + errorMessage)) { return false; } // check extra generator: - if (!testValue(state, "CMAKE_EXTRA_GENERATOR", extraGenerator, - "extra generator", errorMessage)) { + if (!getOrTestValue(state, "CMAKE_EXTRA_GENERATOR", extraGenerator, + "extra generator", errorMessage)) { return false; } // check sourcedir: - if (!testHomeDirectory(state, sourceDirectory, errorMessage)) { + if (!getOrTestHomeDirectory(state, sourceDirectory, errorMessage)) { return false; } // check toolset: - if (!testValue(state, "CMAKE_GENERATOR_TOOLSET", toolset, "toolset", - errorMessage)) { + if (!getOrTestValue(state, "CMAKE_GENERATOR_TOOLSET", toolset, "toolset", + errorMessage)) { return false; } // check platform: - if (!testValue(state, "CMAKE_GENERATOR_PLATFORM", platform, "platform", - errorMessage)) { + if (!getOrTestValue(state, "CMAKE_GENERATOR_PLATFORM", platform, + "platform", errorMessage)) { return false; } } @@ -708,9 +709,31 @@ static Json::Value DumpSourceFilesList( lg->AppendFlags(compileFlags, genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = file->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + compileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + } fileData.Flags = compileFlags; - fileData.IncludePathList = ld.IncludePathList; + // Add include directories from source file properties. + std::vector<std::string> includes; + + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = file->GetProperty(INCLUDE_DIRECTORIES)) { + const char* evaluatedIncludes = + genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); + lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); + + for (const auto& include : includes) { + fileData.IncludePathList.push_back(std::make_pair( + include, target->IsSystemIncludeDirectory(include, config))); + } + } + + fileData.IncludePathList.insert(fileData.IncludePathList.end(), + ld.IncludePathList.begin(), + ld.IncludePathList.end()); const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); std::set<std::string> defines; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index bdef3e5..479ecd2 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -386,6 +386,12 @@ void cmStateSnapshot::InitializeFromParent() parent->BuildSystemDirectory->CompileOptionsBacktraces, this->Position->BuildSystemDirectory->CompileOptionsBacktraces, this->Position->CompileOptionsPosition); + + const char* include_regex = + parent->BuildSystemDirectory->Properties.GetPropertyValue( + "INCLUDE_REGULAR_EXPRESSION"); + this->Position->BuildSystemDirectory->Properties.SetProperty( + "INCLUDE_REGULAR_EXPRESSION", include_regex); } cmState* cmStateSnapshot::GetState() const diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index d7ff58c..323131a 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -52,6 +52,8 @@ // include wincrypt.h after windows.h #include <wincrypt.h> +#include <fcntl.h> /* _O_TEXT */ + #include "cm_uv.h" #else #include <sys/time.h> @@ -981,6 +983,21 @@ std::string cmSystemTools::GetRealPath(const std::string& path, } #endif +void cmSystemTools::InitializeLibUV() +{ +#if defined(_WIN32) + // Perform libuv one-time initialization now, and then un-do its + // global _fmode setting so that using libuv does not change the + // default file text/binary mode. See libuv issue 840. + uv_loop_close(uv_default_loop()); +#ifdef _MSC_VER + _set_fmode(_O_TEXT); +#else + _fmode = _O_TEXT; +#endif +#endif +} + bool cmSystemTools::RenameFile(const char* oldname, const char* newname) { #ifdef _WIN32 @@ -2093,8 +2110,12 @@ void cmSystemTools::FindCMakeResources(const char* argv0) (void)argv0; // ignore this on windows wchar_t modulepath[_MAX_PATH]; ::GetModuleFileNameW(NULL, modulepath, sizeof(modulepath)); - exe_dir = - cmSystemTools::GetFilenamePath(cmsys::Encoding::ToNarrow(modulepath)); + std::string path = cmsys::Encoding::ToNarrow(modulepath); + std::string realPath = cmSystemTools::GetRealPath(path, NULL); + if (realPath.empty()) { + realPath = path; + } + exe_dir = cmSystemTools::GetFilenamePath(realPath); #elif defined(__APPLE__) (void)argv0; // ignore this on OS X #define CM_EXE_PATH_LOCAL_SIZE 16384 diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 77e34b4..d1e07c5 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -504,6 +504,10 @@ public: static std::string GetRealPath(const std::string& path, std::string* errorMessage = 0); #endif + + /** Perform one-time initialization of libuv. */ + static void InitializeLibUV(); + private: static bool s_ForceUnixPaths; static bool s_RunCommandHideConsole; diff --git a/Source/cmUtils.hxx b/Source/cmUtils.hxx index c5c767c..a7a3e81 100644 --- a/Source/cmUtils.hxx +++ b/Source/cmUtils.hxx @@ -7,7 +7,7 @@ // Use the make system's VERBOSE environment variable to enable // verbose output. This can be skipped by also setting CMAKE_NO_VERBOSE -// (which is set by the Eclipse and KDevelop generators). +// (which is set by the Eclipse generator). inline bool isCMakeVerbose() { return (cmSystemTools::HasEnv("VERBOSE") && diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 1b09600..4555358 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -114,22 +114,6 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator() { - for (OptionsMap::iterator i = this->ClOptions.begin(); - i != this->ClOptions.end(); ++i) { - delete i->second; - } - for (OptionsMap::iterator i = this->LinkOptions.begin(); - i != this->LinkOptions.end(); ++i) { - delete i->second; - } - for (OptionsMap::iterator i = this->CudaOptions.begin(); - i != this->CudaOptions.end(); ++i) { - delete i->second; - } - for (OptionsMap::iterator i = this->CudaLinkOptions.begin(); - i != this->CudaLinkOptions.end(); ++i) { - delete i->second; - } if (!this->BuildFileStream) { return; } @@ -1366,7 +1350,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() si != sources.end(); ++si) { std::string const& source = si->Source->GetFullPath(); cmSourceGroup* sourceGroup = - this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); + this->Makefile->FindSourceGroup(source, sourceGroups); groupsUsed.insert(sourceGroup); } @@ -1538,7 +1522,7 @@ void cmVisualStudio10TargetGenerator::WriteGroupSources( cmSourceFile const* sf = s.SourceFile; std::string const& source = sf->GetFullPath(); cmSourceGroup* sourceGroup = - this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); + this->Makefile->FindSourceGroup(source, sourceGroups); std::string const& filter = sourceGroup->GetFullName(); this->WriteString("<", 2); std::string path = this->ConvertPath(source, s.RelativePath); @@ -1992,7 +1976,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() if (si.Kind == cmGeneratorTarget::SourceKindObjectSource) { // FIXME: refactor generation to avoid tracking XML syntax state. - this->WriteSource(tool, si.Source, " "); + this->WriteSource(tool, si.Source, ""); bool have_nested = this->OutputSourceSpecificFlags(si.Source); if (!exclude_configs.empty()) { if (!have_nested) { @@ -2036,18 +2020,32 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } std::string flags; bool configDependentFlags = false; + std::string options; + bool configDependentOptions = false; std::string defines; bool configDependentDefines = false; + std::string includes; + bool configDependentIncludes = false; if (const char* cflags = sf.GetProperty("COMPILE_FLAGS")) { configDependentFlags = cmGeneratorExpression::Find(cflags) != std::string::npos; flags += cflags; } + if (const char* coptions = sf.GetProperty("COMPILE_OPTIONS")) { + configDependentOptions = + cmGeneratorExpression::Find(coptions) != std::string::npos; + options += coptions; + } if (const char* cdefs = sf.GetProperty("COMPILE_DEFINITIONS")) { configDependentDefines = cmGeneratorExpression::Find(cdefs) != std::string::npos; defines += cdefs; } + if (const char* cincludes = sf.GetProperty("INCLUDE_DIRECTORIES")) { + configDependentIncludes = + cmGeneratorExpression::Find(cincludes) != std::string::npos; + includes += cincludes; + } std::string lang = this->GlobalGenerator->GetLanguageFromExtension(sf.GetExtension().c_str()); std::string sourceLang = this->LocalGenerator->GetSourceFileLanguage(sf); @@ -2099,7 +2097,8 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } // if we have flags or defines for this config then // use them - if (!flags.empty() || !configDefines.empty() || compileAs || noWinRT) { + if (!flags.empty() || !options.empty() || !configDefines.empty() || + !includes.empty() || compileAs || noWinRT) { (*this->BuildFileStream) << firstString; firstString = ""; // only do firstString once hasFlags = true; @@ -2137,9 +2136,16 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } else { clOptions.Parse(flags.c_str()); } - if (clOptions.HasFlag("AdditionalIncludeDirectories")) { - clOptions.AppendFlag("AdditionalIncludeDirectories", - "%(AdditionalIncludeDirectories)"); + if (!options.empty()) { + std::string expandedOptions; + if (configDependentOptions) { + this->LocalGenerator->AppendCompileOptions( + expandedOptions, + genexInterpreter.Evaluate(options, "COMPILE_OPTIONS")); + } else { + this->LocalGenerator->AppendCompileOptions(expandedOptions, options); + } + clOptions.Parse(expandedOptions.c_str()); } if (clOptions.HasFlag("DisableSpecificWarnings")) { clOptions.AppendFlag("DisableSpecificWarnings", @@ -2151,9 +2157,21 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } else { clOptions.AddDefines(configDefines.c_str()); } + std::vector<std::string> includeList; + if (configDependentIncludes) { + this->LocalGenerator->AppendIncludeDirectories( + includeList, + genexInterpreter.Evaluate(includes, "INCLUDE_DIRECTORIES"), *source); + } else { + this->LocalGenerator->AppendIncludeDirectories(includeList, includes, + *source); + } + clOptions.AddIncludes(includeList); clOptions.SetConfiguration(config.c_str()); clOptions.PrependInheritedString("AdditionalOptions"); clOptions.OutputFlagMap(*this->BuildFileStream, " "); + clOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, + " ", "\n", lang); clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", lang); } @@ -2446,6 +2464,13 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( break; } clOptions.AddDefines(targetDefines); + + // Get includes for this target + if (!this->LangForClCompile.empty()) { + clOptions.AddIncludes( + this->GetIncludes(configName, this->LangForClCompile)); + } + if (this->MSTools) { clOptions.SetVerboseMakefile( this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")); @@ -2497,7 +2522,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } } - this->ClOptions[configName] = pOptions.release(); + this->ClOptions[configName] = std::move(pOptions); return true; } @@ -2510,14 +2535,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( } this->WriteString("<ClCompile>\n", 2); clOptions.PrependInheritedString("AdditionalOptions"); - if (!this->LangForClCompile.empty()) { - std::vector<std::string> const includes = - this->GetIncludes(configName, this->LangForClCompile); - clOptions.AppendFlag("AdditionalIncludeDirectories", includes); - } - clOptions.AppendFlag("AdditionalIncludeDirectories", - "%(AdditionalIncludeDirectories)"); clOptions.OutputFlagMap(*this->BuildFileStream, " "); + clOptions.OutputAdditionalIncludeDirectories( + *this->BuildFileStream, " ", "\n", this->LangForClCompile); clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", this->LangForClCompile); @@ -2595,7 +2615,10 @@ bool cmVisualStudio10TargetGenerator::ComputeRcOptions( Options& clOptions = *(this->ClOptions[configName]); rcOptions.AddDefines(clOptions.GetDefines()); - this->RcOptions[configName] = pOptions.release(); + // Get includes for this target + rcOptions.AddIncludes(this->GetIncludes(configName, "RC")); + + this->RcOptions[configName] = std::move(pOptions); return true; } @@ -2610,11 +2633,8 @@ void cmVisualStudio10TargetGenerator::WriteRCOptions( Options& rcOptions = *(this->RcOptions[configName]); rcOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", "RC"); - std::vector<std::string> const includes = - this->GetIncludes(configName, "RC"); - rcOptions.AppendFlag("AdditionalIncludeDirectories", includes); - rcOptions.AppendFlag("AdditionalIncludeDirectories", - "%(AdditionalIncludeDirectories)"); + rcOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, + " ", "\n", "RC"); rcOptions.PrependInheritedString("AdditionalOptions"); rcOptions.OutputFlagMap(*this->BuildFileStream, " "); @@ -2728,7 +2748,10 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( cudaOptions.AddDefine(exportMacro); } - this->CudaOptions[configName] = pOptions.release(); + // Get includes for this target + cudaOptions.AddIncludes(this->GetIncludes(configName, "CUDA")); + + this->CudaOptions[configName] = std::move(pOptions); return true; } @@ -2741,10 +2764,8 @@ void cmVisualStudio10TargetGenerator::WriteCudaOptions( this->WriteString("<CudaCompile>\n", 2); Options& cudaOptions = *(this->CudaOptions[configName]); - std::vector<std::string> const includes = - this->GetIncludes(configName, "CUDA"); - cudaOptions.AppendFlag("Include", includes); - cudaOptions.AppendFlag("Include", "%(Include)"); + cudaOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, + " ", "\n", "CUDA"); cudaOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", "CUDA"); cudaOptions.PrependInheritedString("AdditionalOptions"); @@ -2801,7 +2822,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( "-Wno-deprecated-gpu-targets"); } - this->CudaLinkOptions[configName] = pOptions.release(); + this->CudaLinkOptions[configName] = std::move(pOptions); return true; } @@ -2852,7 +2873,11 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( std::string(this->Makefile->GetSafeDefinition(configFlagsVar)); masmOptions.Parse(flags.c_str()); - this->MasmOptions[configName] = pOptions.release(); + + // Get includes for this target + masmOptions.AddIncludes(this->GetIncludes(configName, "ASM_MASM")); + + this->MasmOptions[configName] = std::move(pOptions); return true; } @@ -2870,10 +2895,8 @@ void cmVisualStudio10TargetGenerator::WriteMasmOptions( "\n", "ASM_MASM"); Options& masmOptions = *(this->MasmOptions[configName]); - std::vector<std::string> const includes = - this->GetIncludes(configName, "ASM_MASM"); - masmOptions.AppendFlag("IncludePaths", includes); - masmOptions.AppendFlag("IncludePaths", "%(IncludePaths)"); + masmOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, + " ", "\n", "ASM_MASM"); masmOptions.PrependInheritedString("AdditionalOptions"); masmOptions.OutputFlagMap(*this->BuildFileStream, " "); @@ -2911,7 +2934,11 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( std::string(" ") + std::string(this->Makefile->GetSafeDefinition(configFlagsVar)); nasmOptions.Parse(flags.c_str()); - this->NasmOptions[configName] = pOptions.release(); + + // Get includes for this target + nasmOptions.AddIncludes(this->GetIncludes(configName, "ASM_NASM")); + + this->NasmOptions[configName] = std::move(pOptions); return true; } @@ -2926,12 +2953,8 @@ void cmVisualStudio10TargetGenerator::WriteNasmOptions( std::vector<std::string> includes = this->GetIncludes(configName, "ASM_NASM"); Options& nasmOptions = *(this->NasmOptions[configName]); - for (size_t i = 0; i < includes.size(); i++) { - includes[i] += "\\"; - } - - nasmOptions.AppendFlag("IncludePaths", includes); - nasmOptions.AppendFlag("IncludePaths", "%(IncludePaths)"); + nasmOptions.OutputAdditionalIncludeDirectories(*this->BuildFileStream, + " ", "\n", "ASM_NASM"); nasmOptions.OutputFlagMap(*this->BuildFileStream, " "); nasmOptions.PrependInheritedString("AdditionalOptions"); nasmOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", @@ -3357,7 +3380,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( } } - this->LinkOptions[config] = pOptions.release(); + this->LinkOptions[config] = std::move(pOptions); return true; } @@ -3523,7 +3546,7 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() if (this->ProjectType == csproj) { return; } - for (std::string const& i : this->Configurations) { + for (const auto& i : this->Configurations) { this->WritePlatformConfigTag("ItemDefinitionGroup", i, 1); *this->BuildFileStream << "\n"; // output cl compile flags <ClCompile></ClCompile> diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index c346164..2bc5da9 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -7,6 +7,7 @@ #include <iosfwd> #include <map> +#include <memory> #include <set> #include <string> #include <vector> @@ -24,6 +25,8 @@ class cmVisualStudioGeneratorOptions; class cmVisualStudio10TargetGenerator { + CM_DISABLE_COPY(cmVisualStudio10TargetGenerator) + public: cmVisualStudio10TargetGenerator(cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg); @@ -169,7 +172,7 @@ private: private: typedef cmVisualStudioGeneratorOptions Options; - typedef std::map<std::string, Options*> OptionsMap; + typedef std::map<std::string, std::unique_ptr<Options>> OptionsMap; OptionsMap ClOptions; OptionsMap RcOptions; OptionsMap CudaOptions; diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 106bdff..ccbff83 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -29,23 +29,8 @@ static std::string cmVisualStudioGeneratorOptionsEscapeForXML(std::string ret) cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( cmLocalVisualStudioGenerator* lg, Tool tool, cmVisualStudio10TargetGenerator* g) - : cmIDEOptions() - , LocalGenerator(lg) - , Version(lg->GetVersion()) - , CurrentTool(tool) - , TargetGenerator(g) + : cmVisualStudioGeneratorOptions(lg, tool, nullptr, nullptr, g) { - // Preprocessor definitions are not allowed for linker tools. - this->AllowDefine = (tool != Linker); - - // Slash options are allowed for VS. - this->AllowSlash = true; - - this->FortranRuntimeDebug = false; - this->FortranRuntimeDLL = false; - this->FortranRuntimeMT = false; - - this->UnknownFlagField = "AdditionalOptions"; } cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( @@ -64,6 +49,9 @@ cmVisualStudioGeneratorOptions::cmVisualStudioGeneratorOptions( // Preprocessor definitions are not allowed for linker tools. this->AllowDefine = (tool != Linker); + // include directories are not allowed for linker tools. + this->AllowInclude = (tool != Linker); + // Slash options are allowed for VS. this->AllowSlash = true; @@ -511,6 +499,69 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( } } +void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( + std::ostream& fout, const char* prefix, const char* suffix, + const std::string& lang) +{ + if (this->Includes.empty()) { + return; + } + + const char* tag = "AdditionalIncludeDirectories"; + if (lang == "CUDA") { + tag = "Include"; + } else if (lang == "ASM_MASM" || lang == "ASM_NASM") { + tag = "IncludePaths"; + } + + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + // if there are configuration specific flags, then + // use the configuration specific tag for PreprocessorDefinitions + if (!this->Configuration.empty()) { + fout << prefix; + this->TargetGenerator->WritePlatformConfigTag( + tag, this->Configuration.c_str(), 0, 0, 0, &fout); + } else { + fout << prefix << "<" << tag << ">"; + } + } else { + fout << prefix << tag << "=\""; + } + + const char* sep = ""; + for (std::string include : this->Includes) { + // first convert all of the slashes + std::string::size_type pos = 0; + while ((pos = include.find('/', pos)) != std::string::npos) { + include[pos] = '\\'; + pos++; + } + + if (lang == "ASM_NASM") { + include += "\\"; + } + + // Escape this include for the IDE. + fout << sep << (this->Version >= cmGlobalVisualStudioGenerator::VS10 + ? cmVisualStudio10GeneratorOptionsEscapeForXML(include) + : cmVisualStudioGeneratorOptionsEscapeForXML(include)); + sep = ";"; + + if (lang == "Fortran") { + include += "/$(ConfigurationName)"; + fout << sep << (this->Version >= cmGlobalVisualStudioGenerator::VS10 + ? cmVisualStudio10GeneratorOptionsEscapeForXML(include) + : cmVisualStudioGeneratorOptionsEscapeForXML(include)); + } + } + + if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { + fout << sep << "%(" << tag << ")</" << tag << ">" << suffix; + } else { + fout << "\"" << suffix; + } +} + void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout, const char* indent) { diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h index 7c08a2c..2dffe9b 100644 --- a/Source/cmVisualStudioGeneratorOptions.h +++ b/Source/cmVisualStudioGeneratorOptions.h @@ -86,6 +86,10 @@ public: void OutputPreprocessorDefinitions(std::ostream& fout, const char* prefix, const char* suffix, const std::string& lang); + void OutputAdditionalIncludeDirectories(std::ostream& fout, + const char* prefix, + const char* suffix, + const std::string& lang); void OutputFlagMap(std::ostream& fout, const char* indent); void SetConfiguration(const char* config); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 48e3ddf..f488552 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -35,14 +35,6 @@ #include <unordered_map> #endif -// only build kdevelop generator on non-windows platforms -// when not bootstrapping cmake -#if !defined(_WIN32) -#if defined(CMAKE_BUILD_WITH_CMAKE) -#define CMAKE_USE_KDEVELOP -#endif -#endif - #if defined(CMAKE_BUILD_WITH_CMAKE) #define CMAKE_USE_ECLIPSE #endif @@ -88,10 +80,6 @@ #include "cmExtraKateGenerator.h" #include "cmExtraSublimeTextGenerator.h" -#ifdef CMAKE_USE_KDEVELOP -#include "cmGlobalKdevelopGenerator.h" -#endif - #ifdef CMAKE_USE_ECLIPSE #include "cmExtraEclipseCDT4Generator.h" #endif @@ -757,8 +745,13 @@ void cmake::SetArgs(const std::vector<std::string>& args, } cmGlobalGenerator* gen = this->CreateGlobalGenerator(value); if (!gen) { + const char* kdevError = nullptr; + if (value.find("KDevelop3", 0) != std::string::npos) { + kdevError = "\nThe KDevelop3 generator is not supported anymore."; + } + cmSystemTools::Error("Could not create named generator ", - value.c_str()); + value.c_str(), kdevError); this->PrintGeneratorList(); } else { this->SetGlobalGenerator(gen); @@ -903,9 +896,6 @@ void cmake::AddDefaultExtraGenerators() this->ExtraGenerators.push_back(cmExtraEclipseCDT4Generator::GetFactory()); #endif -#ifdef CMAKE_USE_KDEVELOP - this->ExtraGenerators.push_back(cmGlobalKdevelopGenerator::GetFactory()); -#endif #endif } diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 59b908a..b185a1b 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -16,10 +16,6 @@ #include "cmDynamicLoader.h" #endif -#ifdef _WIN32 -#include <fcntl.h> /* _O_TEXT */ -#include <stdlib.h> /* _set_fmode, _fmode */ -#endif #include "cm_uv.h" #include "cmsys/Encoding.hxx" @@ -170,19 +166,8 @@ int main(int ac, char const* const* av) ac = args.argc(); av = args.argv(); -#if defined(_WIN32) - // Perform libuv one-time initialization now, and then un-do its - // global _fmode setting so that using libuv does not change the - // default file text/binary mode. See libuv issue 840. - uv_loop_close(uv_default_loop()); -#ifdef _MSC_VER - _set_fmode(_O_TEXT); -#else - _fmode = _O_TEXT; -#endif -#endif - cmSystemTools::EnableMSVCDebugHook(); + cmSystemTools::InitializeLibUV(); cmSystemTools::FindCMakeResources(av[0]); if (ac > 1) { if (strcmp(av[1], "--build") == 0) { diff --git a/Source/ctest.cxx b/Source/ctest.cxx index ad5fec0..0a6d1d2 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -137,6 +137,7 @@ int main(int argc, char const* const* argv) cmSystemTools::DoNotInheritStdPipes(); cmSystemTools::EnableMSVCDebugHook(); + cmSystemTools::InitializeLibUV(); cmSystemTools::FindCMakeResources(argv[0]); // Dispatch 'ctest --launch' mode directly. diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index 351e38d..2570e5b 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -83,7 +83,9 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6.3 FATAL_ERROR) FOREACH(p + CMP0022 # CMake 2.8, Define link interface - required by android_mk export CMP0025 # CMake 3.0, Compiler id for Apple Clang is now AppleClang. + CMP0042 # CMake 3.0, MACOSX_RPATH is enabled by default. CMP0048 # CMake 3.0, Let the project command manage version variables. CMP0056 # CMake 3.2, Honor link flags in try_compile() source-file signature. CMP0063 # CMake 3.3, Honor visibility properties for all target types. @@ -494,6 +496,14 @@ IF(KWSYS_USE_SystemTools) KWSYS_CXX_STAT_HAS_ST_MTIM=${KWSYS_CXX_STAT_HAS_ST_MTIM} KWSYS_CXX_STAT_HAS_ST_MTIMESPEC=${KWSYS_CXX_STAT_HAS_ST_MTIMESPEC} ) + + # Disable getpwnam for static linux builds since it depends on shared glibc + GET_PROPERTY(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + IF(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT SHARED_LIBS_SUPPORTED) + SET_PROPERTY(SOURCE SystemTools.cxx APPEND PROPERTY COMPILE_DEFINITIONS + HAVE_GETPWNAM=0 + ) + ENDIF() ENDIF() IF(KWSYS_USE_SystemInformation) @@ -576,7 +586,7 @@ IF(KWSYS_USE_SystemInformation) CHECK_INCLUDE_FILE_CXX("execinfo.h" KWSYS_CXX_HAS_EXECINFOH) IF (KWSYS_CXX_HAS_EXECINFOH) # we have the backtrace header check if it - # can be used with this compiler + # can be used with this compiler SET(KWSYS_PLATFORM_CXX_TEST_LINK_LIBRARIES ${EXECINFO_LIB}) KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_BACKTRACE "Checking whether backtrace works with this C++ compiler" DIRECT) @@ -1012,11 +1022,11 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) # C tests SET(KWSYS_C_TESTS - testEncode - testTerminal + testEncode.c + testTerminal.c ) IF(KWSYS_STANDALONE) - SET(KWSYS_C_TESTS ${KWSYS_C_TESTS} testFail) + SET(KWSYS_C_TESTS ${KWSYS_C_TESTS} testFail.c) ENDIF() CREATE_TEST_SOURCELIST( KWSYS_C_TEST_SRCS ${KWSYS_NAMESPACE}TestsC.c @@ -1025,7 +1035,8 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) ADD_EXECUTABLE(${KWSYS_NAMESPACE}TestsC ${KWSYS_C_TEST_SRCS}) SET_PROPERTY(TARGET ${KWSYS_NAMESPACE}TestsC PROPERTY LABELS ${KWSYS_LABELS_EXE}) TARGET_LINK_LIBRARIES(${KWSYS_NAMESPACE}TestsC ${KWSYS_TARGET_C_LINK}) - FOREACH(test ${KWSYS_C_TESTS}) + FOREACH(testfile ${KWSYS_C_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) ADD_TEST(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsC ${test} ${KWSYS_TEST_ARGS_${test}}) SET_PROPERTY(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST}) ENDFOREACH() @@ -1033,24 +1044,24 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) # C++ tests IF(NOT WATCOM AND NOT CMake_SOURCE_DIR) SET(KWSYS_CXX_TESTS - testHashSTL + testHashSTL.cxx ) ENDIF() SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} - testConfigure - testSystemTools - testCommandLineArguments - testCommandLineArguments1 - testDirectory + testConfigure.cxx + testSystemTools.cxx + testCommandLineArguments.cxx + testCommandLineArguments1.cxx + testDirectory.cxx ) IF(KWSYS_STL_HAS_WSTRING) SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} - testEncoding + testEncoding.cxx ) ENDIF() IF(KWSYS_USE_FStream) SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} - testFStream + testFStream.cxx ) ENDIF() IF(KWSYS_USE_ConsoleBuf) @@ -1062,7 +1073,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) SET_PROPERTY(TARGET testConsoleBufChild PROPERTY LABELS ${KWSYS_LABELS_EXE}) TARGET_LINK_LIBRARIES(testConsoleBufChild ${KWSYS_TARGET_LINK}) SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} - testConsoleBuf + testConsoleBuf.cxx ) IF("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "19.0.23506") @@ -1072,10 +1083,10 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) ENDIF() IF(KWSYS_USE_SystemInformation) - SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation) + SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation.cxx) ENDIF() IF(KWSYS_USE_DynamicLoader) - SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testDynamicLoader) + SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testDynamicLoader.cxx) # If kwsys contains the DynamicLoader, need extra library ADD_LIBRARY(${KWSYS_NAMESPACE}TestDynload MODULE testDynload.c) SET_PROPERTY(TARGET ${KWSYS_NAMESPACE}TestDynload PROPERTY LABELS ${KWSYS_LABELS_LIB}) @@ -1134,7 +1145,8 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) -p some junk at the end ) - FOREACH(test ${KWSYS_CXX_TESTS}) + FOREACH(testfile ${KWSYS_CXX_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) ADD_TEST(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsCxx ${test} ${KWSYS_TEST_ARGS_${test}}) SET_PROPERTY(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST}) ENDFOREACH() diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx index 5792da9..a6387ea 100644 --- a/Source/kwsys/CommandLineArguments.cxx +++ b/Source/kwsys/CommandLineArguments.cxx @@ -68,8 +68,8 @@ class CommandLineArgumentsInternal public: CommandLineArgumentsInternal() { - this->UnknownArgumentCallback = 0; - this->ClientData = 0; + this->UnknownArgumentCallback = KWSYS_NULLPTR; + this->ClientData = KWSYS_NULLPTR; this->LastArgument = 0; } @@ -187,7 +187,7 @@ int CommandLineArguments::Parse() switch (cs->ArgumentType) { case NO_ARGUMENT: // No value - if (!this->PopulateVariable(cs, 0)) { + if (!this->PopulateVariable(cs, KWSYS_NULLPTR)) { return 0; } break; @@ -340,7 +340,7 @@ void CommandLineArguments::AddCallback(const char* argument, s.Callback = callback; s.CallData = call_data; s.VariableType = CommandLineArguments::NO_VARIABLE_TYPE; - s.Variable = 0; + s.Variable = KWSYS_NULLPTR; s.Help = help; this->Internals->Callbacks[argument] = s; @@ -355,8 +355,8 @@ void CommandLineArguments::AddArgument(const char* argument, CommandLineArgumentsCallbackStructure s; s.Argument = argument; s.ArgumentType = type; - s.Callback = 0; - s.CallData = 0; + s.Callback = KWSYS_NULLPTR; + s.CallData = KWSYS_NULLPTR; s.VariableType = vtype; s.Variable = variable; s.Help = help; @@ -427,7 +427,7 @@ const char* CommandLineArguments::GetHelp(const char* arg) CommandLineArguments::Internal::CallbacksMap::iterator it = this->Internals->Callbacks.find(arg); if (it == this->Internals->Callbacks.end()) { - return 0; + return KWSYS_NULLPTR; } // Since several arguments may point to the same argument, find the one this @@ -621,7 +621,7 @@ void CommandLineArguments::PopulateVariable(bool* variable, void CommandLineArguments::PopulateVariable(int* variable, const std::string& value) { - char* res = 0; + char* res = KWSYS_NULLPTR; *variable = static_cast<int>(strtol(value.c_str(), &res, 10)); // if ( res && *res ) // { @@ -632,7 +632,7 @@ void CommandLineArguments::PopulateVariable(int* variable, void CommandLineArguments::PopulateVariable(double* variable, const std::string& value) { - char* res = 0; + char* res = KWSYS_NULLPTR; *variable = strtod(value.c_str(), &res); // if ( res && *res ) // { @@ -669,7 +669,7 @@ void CommandLineArguments::PopulateVariable(std::vector<bool>* variable, void CommandLineArguments::PopulateVariable(std::vector<int>* variable, const std::string& value) { - char* res = 0; + char* res = KWSYS_NULLPTR; variable->push_back(static_cast<int>(strtol(value.c_str(), &res, 10))); // if ( res && *res ) // { @@ -680,7 +680,7 @@ void CommandLineArguments::PopulateVariable(std::vector<int>* variable, void CommandLineArguments::PopulateVariable(std::vector<double>* variable, const std::string& value) { - char* res = 0; + char* res = KWSYS_NULLPTR; variable->push_back(strtod(value.c_str(), &res)); // if ( res && *res ) // { diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in index 1e67874..05afc7d 100644 --- a/Source/kwsys/Configure.hxx.in +++ b/Source/kwsys/Configure.hxx.in @@ -20,6 +20,12 @@ #define @KWSYS_NAMESPACE@__has_cpp_attribute(x) 0 #endif +#if __cplusplus >= 201103L +#define @KWSYS_NAMESPACE@_NULLPTR nullptr +#else +#define @KWSYS_NAMESPACE@_NULLPTR 0 +#endif + #ifndef @KWSYS_NAMESPACE@_FALLTHROUGH #if __cplusplus >= 201703L && @KWSYS_NAMESPACE@__has_cpp_attribute(fallthrough) #define @KWSYS_NAMESPACE@_FALLTHROUGH [[fallthrough]] @@ -48,6 +54,7 @@ #define KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H \ @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H #define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH +#define KWSYS_NULLPTR @KWSYS_NAMESPACE@_NULLPTR #endif #endif diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index 69068aa..a84be11 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -48,7 +48,7 @@ unsigned long Directory::GetNumberOfFiles() const const char* Directory::GetFile(unsigned long dindex) const { if (dindex >= this->Internal->Files.size()) { - return 0; + return KWSYS_NULLPTR; } return this->Internal->Files[dindex].c_str(); } diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx index 664f183..9b7d9bf 100644 --- a/Source/kwsys/DynamicLoader.cxx +++ b/Source/kwsys/DynamicLoader.cxx @@ -60,7 +60,7 @@ const char* DynamicLoader::LastError() } // namespace KWSYS_NAMESPACE #elif defined(__hpux) -// Implementation for HPUX machines +// Implementation for HPUX machines #include <dl.h> #include <errno.h> diff --git a/Source/kwsys/EncodingCXX.cxx b/Source/kwsys/EncodingCXX.cxx index b1e54c9..a1fe040 100644 --- a/Source/kwsys/EncodingCXX.cxx +++ b/Source/kwsys/EncodingCXX.cxx @@ -65,7 +65,7 @@ Encoding::CommandLineArguments::CommandLineArguments(int ac, for (int i = 0; i < ac; i++) { this->argv_[i] = strdup(av[i]); } - this->argv_[ac] = 0; + this->argv_[ac] = KWSYS_NULLPTR; } Encoding::CommandLineArguments::CommandLineArguments(int ac, @@ -75,7 +75,7 @@ Encoding::CommandLineArguments::CommandLineArguments(int ac, for (int i = 0; i < ac; i++) { this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]); } - this->argv_[ac] = 0; + this->argv_[ac] = KWSYS_NULLPTR; } Encoding::CommandLineArguments::~CommandLineArguments() @@ -90,7 +90,7 @@ Encoding::CommandLineArguments::CommandLineArguments( { this->argv_.resize(other.argv_.size()); for (size_t i = 0; i < this->argv_.size(); i++) { - this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : 0; + this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : KWSYS_NULLPTR; } } @@ -105,7 +105,7 @@ Encoding::CommandLineArguments& Encoding::CommandLineArguments::operator=( this->argv_.resize(other.argv_.size()); for (i = 0; i < this->argv_.size(); i++) { - this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : 0; + this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : KWSYS_NULLPTR; } } @@ -193,7 +193,7 @@ std::string Encoding::ToNarrow(const std::wstring& str) std::wstring Encoding::ToWide(const char* cstr) { std::wstring wstr; - size_t length = kwsysEncoding_mbstowcs(0, cstr, 0) + 1; + size_t length = kwsysEncoding_mbstowcs(KWSYS_NULLPTR, cstr, 0) + 1; if (length > 0) { std::vector<wchar_t> wchars(length); if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) { @@ -206,7 +206,7 @@ std::wstring Encoding::ToWide(const char* cstr) std::string Encoding::ToNarrow(const wchar_t* wcstr) { std::string str; - size_t length = kwsysEncoding_wcstombs(0, wcstr, 0) + 1; + size_t length = kwsysEncoding_wcstombs(KWSYS_NULLPTR, wcstr, 0) + 1; if (length > 0) { std::vector<char> chars(length); if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) { diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx index d2f0b85..2b6db78 100644 --- a/Source/kwsys/Glob.cxx +++ b/Source/kwsys/Glob.cxx @@ -28,7 +28,7 @@ #include <string.h> namespace KWSYS_NAMESPACE { #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) -// On Windows and apple, no difference between lower and upper case +// On Windows and Apple, no difference between lower and upper case #define KWSYS_GLOB_CASE_INDEPENDENT #endif @@ -81,13 +81,13 @@ std::string Glob::PatternToRegex(const std::string& pattern, int c = *i; if (c == '*') { // A '*' (not between brackets) matches any string. - // We modify this to not match slashes since the orignal glob + // We modify this to not match slashes since the original glob // pattern documentation was meant for matching file name // components separated by slashes. regex += "[^/]*"; } else if (c == '?') { // A '?' (not between brackets) matches any single character. - // We modify this to not match slashes since the orignal glob + // We modify this to not match slashes since the original glob // pattern documentation was meant for matching file name // components separated by slashes. regex += "[^/]"; @@ -201,7 +201,7 @@ bool Glob::RecurseDirectory(std::string::size_type start, } #if defined(KWSYS_GLOB_CASE_INDEPENDENT) - // On Windows and apple, no difference between lower and upper case + // On Windows and Apple, no difference between lower and upper case fname = kwsys::SystemTools::LowerCase(fname); #endif @@ -430,7 +430,7 @@ void Glob::SetRelative(const char* dir) const char* Glob::GetRelative() { if (this->Relative.empty()) { - return 0; + return KWSYS_NULLPTR; } return this->Relative.c_str(); } diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index 718a1aa..1e80b39 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -231,7 +231,7 @@ struct kwsysProcess_s when reaping PIDs or modifying this array to avoid race conditions. */ volatile pid_t* volatile ForkPIDs; - /* Flag for whether the children were terminated by a faild select. */ + /* Flag for whether the children were terminated by a failed select. */ int SelectError; /* The timeout length. */ diff --git a/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx index fa3551c..78cff1a 100644 --- a/Source/kwsys/RegularExpression.cxx +++ b/Source/kwsys/RegularExpression.cxx @@ -37,18 +37,18 @@ namespace KWSYS_NAMESPACE { RegularExpression::RegularExpression(const RegularExpression& rxp) { if (!rxp.program) { - this->program = 0; + this->program = KWSYS_NULLPTR; return; } int ind; this->progsize = rxp.progsize; // Copy regular expression size this->program = new char[this->progsize]; // Allocate storage - for (ind = this->progsize; ind-- != 0;) // Copy regular expresion + for (ind = this->progsize; ind-- != 0;) // Copy regular expression this->program[ind] = rxp.program[ind]; // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != 0) { + if (rxp.regmust != KWSYS_NULLPTR) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -69,19 +69,19 @@ RegularExpression& RegularExpression::operator=(const RegularExpression& rxp) return *this; } if (!rxp.program) { - this->program = 0; + this->program = KWSYS_NULLPTR; return *this; } int ind; this->progsize = rxp.progsize; // Copy regular expression size delete[] this->program; this->program = new char[this->progsize]; // Allocate storage - for (ind = this->progsize; ind-- != 0;) // Copy regular expresion + for (ind = this->progsize; ind-- != 0;) // Copy regular expression this->program[ind] = rxp.program[ind]; // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != 0) { + if (rxp.regmust != KWSYS_NULLPTR) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -128,8 +128,8 @@ bool RegularExpression::deep_equal(const RegularExpression& rxp) const this->regmatch.end() == rxp.regmatch.end()); } -// The remaining code in this file is derived from the regular expression code -// whose copyright statement appears below. It has been changed to work +// The remaining code in this file is derived from the regular expression code +// whose copyright statement appears below. It has been changed to work // with the class concepts of C++ and COOL. /* @@ -335,7 +335,7 @@ bool RegularExpression::compile(const char* exp) size_t len; int flags; - if (exp == 0) { + if (exp == KWSYS_NULLPTR) { // RAISE Error, SYM(RegularExpression), SYM(No_Expr), printf("RegularExpression::compile(): No expression supplied.\n"); return false; @@ -363,13 +363,13 @@ bool RegularExpression::compile(const char* exp) // Allocate space. //#ifndef _WIN32 - if (this->program != 0) + if (this->program != KWSYS_NULLPTR) delete[] this->program; //#endif this->program = new char[comp.regsize]; this->progsize = static_cast<int>(comp.regsize); - if (this->program == 0) { + if (this->program == KWSYS_NULLPTR) { // RAISE Error, SYM(RegularExpression), SYM(Out_Of_Memory), printf("RegularExpression::compile(): Out of memory.\n"); return false; @@ -385,7 +385,7 @@ bool RegularExpression::compile(const char* exp) // Dig out information for optimizations. this->regstart = '\0'; // Worst-case defaults. this->reganch = 0; - this->regmust = 0; + this->regmust = KWSYS_NULLPTR; this->regmlen = 0; scan = this->program + 1; // First BRANCH. if (OP(regnext(scan)) == END) { // Only one top-level choice. @@ -406,9 +406,9 @@ bool RegularExpression::compile(const char* exp) // absence of others. // if (flags & SPSTART) { - longest = 0; + longest = KWSYS_NULLPTR; len = 0; - for (; scan != 0; scan = regnext(scan)) + for (; scan != KWSYS_NULLPTR; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); @@ -444,19 +444,19 @@ char* RegExpCompile::reg(int paren, int* flagp) if (regnpar >= RegularExpressionMatch::NSUBEXP) { // RAISE Error, SYM(RegularExpression), SYM(Too_Many_Parens), printf("RegularExpression::compile(): Too many parentheses.\n"); - return 0; + return KWSYS_NULLPTR; } parno = regnpar; regnpar++; ret = regnode(static_cast<char>(OPEN + parno)); } else - ret = 0; + ret = KWSYS_NULLPTR; // Pick up the branches, linking them together. br = regbranch(&flags); - if (br == 0) - return (0); - if (ret != 0) + if (br == KWSYS_NULLPTR) + return (KWSYS_NULLPTR); + if (ret != KWSYS_NULLPTR) regtail(ret, br); // OPEN -> first. else ret = br; @@ -466,8 +466,8 @@ char* RegExpCompile::reg(int paren, int* flagp) while (*regparse == '|') { regparse++; br = regbranch(&flags); - if (br == 0) - return (0); + if (br == KWSYS_NULLPTR) + return (KWSYS_NULLPTR); regtail(ret, br); // BRANCH -> BRANCH. if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH; @@ -479,23 +479,23 @@ char* RegExpCompile::reg(int paren, int* flagp) regtail(ret, ender); // Hook the tails of the branches to the closing node. - for (br = ret; br != 0; br = regnext(br)) + for (br = ret; br != KWSYS_NULLPTR; br = regnext(br)) regoptail(br, ender); // Check for proper termination. if (paren && *regparse++ != ')') { // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens), printf("RegularExpression::compile(): Unmatched parentheses.\n"); - return 0; + return KWSYS_NULLPTR; } else if (!paren && *regparse != '\0') { if (*regparse == ')') { // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Parens), printf("RegularExpression::compile(): Unmatched parentheses.\n"); - return 0; + return KWSYS_NULLPTR; } else { // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), printf("RegularExpression::compile(): Internal error.\n"); - return 0; + return KWSYS_NULLPTR; } // NOTREACHED } @@ -517,19 +517,19 @@ char* RegExpCompile::regbranch(int* flagp) *flagp = WORST; // Tentatively. ret = regnode(BRANCH); - chain = 0; + chain = KWSYS_NULLPTR; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); - if (latest == 0) - return (0); + if (latest == KWSYS_NULLPTR) + return (KWSYS_NULLPTR); *flagp |= flags & HASWIDTH; - if (chain == 0) // First piece. + if (chain == KWSYS_NULLPTR) // First piece. *flagp |= flags & SPSTART; else regtail(chain, latest); chain = latest; } - if (chain == 0) // Loop ran zero times. + if (chain == KWSYS_NULLPTR) // Loop ran zero times. regnode(NOTHING); return (ret); @@ -552,8 +552,8 @@ char* RegExpCompile::regpiece(int* flagp) int flags; ret = regatom(&flags); - if (ret == 0) - return (0); + if (ret == KWSYS_NULLPTR) + return (KWSYS_NULLPTR); op = *regparse; if (!ISMULT(op)) { @@ -564,7 +564,7 @@ char* RegExpCompile::regpiece(int* flagp) if (!(flags & HASWIDTH) && op != '?') { // RAISE Error, SYM(RegularExpression), SYM(Empty_Operand), printf("RegularExpression::compile() : *+ operand could be empty.\n"); - return 0; + return KWSYS_NULLPTR; } *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH); @@ -598,7 +598,7 @@ char* RegExpCompile::regpiece(int* flagp) if (ISMULT(*regparse)) { // RAISE Error, SYM(RegularExpression), SYM(Nested_Operand), printf("RegularExpression::compile(): Nested *?+.\n"); - return 0; + return KWSYS_NULLPTR; } return (ret); } @@ -651,7 +651,7 @@ char* RegExpCompile::regatom(int* flagp) if (rxpclass > rxpclassend + 1) { // RAISE Error, SYM(RegularExpression), SYM(Invalid_Range), printf("RegularExpression::compile(): Invalid range in [].\n"); - return 0; + return KWSYS_NULLPTR; } for (; rxpclass <= rxpclassend; rxpclass++) regc(static_cast<char>(rxpclass)); @@ -664,15 +664,15 @@ char* RegExpCompile::regatom(int* flagp) if (*regparse != ']') { // RAISE Error, SYM(RegularExpression), SYM(Unmatched_Bracket), printf("RegularExpression::compile(): Unmatched [].\n"); - return 0; + return KWSYS_NULLPTR; } regparse++; *flagp |= HASWIDTH | SIMPLE; } break; case '(': ret = reg(1, &flags); - if (ret == 0) - return (0); + if (ret == KWSYS_NULLPTR) + return (KWSYS_NULLPTR); *flagp |= flags & (HASWIDTH | SPSTART); break; case '\0': @@ -680,18 +680,18 @@ char* RegExpCompile::regatom(int* flagp) case ')': // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), printf("RegularExpression::compile(): Internal error.\n"); // Never here - return 0; + return KWSYS_NULLPTR; case '?': case '+': case '*': // RAISE Error, SYM(RegularExpression), SYM(No_Operand), printf("RegularExpression::compile(): ?+* follows nothing.\n"); - return 0; + return KWSYS_NULLPTR; case '\\': if (*regparse == '\0') { // RAISE Error, SYM(RegularExpression), SYM(Trailing_Backslash), printf("RegularExpression::compile(): Trailing backslash.\n"); - return 0; + return KWSYS_NULLPTR; } ret = regnode(EXACTLY); regc(*regparse++); @@ -707,7 +707,7 @@ char* RegExpCompile::regatom(int* flagp) if (len <= 0) { // RAISE Error, SYM(RegularExpression), SYM(Internal_Error), printf("RegularExpression::compile(): Internal error.\n"); - return 0; + return KWSYS_NULLPTR; } ender = *(regparse + len); if (len > 1 && ISMULT(ender)) @@ -805,7 +805,7 @@ void RegExpCompile::regtail(char* p, const char* val) scan = p; for (;;) { temp = regnext(scan); - if (temp == 0) + if (temp == KWSYS_NULLPTR) break; scan = temp; } @@ -824,7 +824,7 @@ void RegExpCompile::regtail(char* p, const char* val) void RegExpCompile::regoptail(char* p, const char* val) { // "Operandless" and "op != BRANCH" are synonymous in practice. - if (p == 0 || p == regdummyptr || OP(p) != BRANCH) + if (p == KWSYS_NULLPTR || p == regdummyptr || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } @@ -874,14 +874,14 @@ bool RegularExpression::find(char const* string, } // If there is a "must appear" string, look for it. - if (this->regmust != 0) { + if (this->regmust != KWSYS_NULLPTR) { s = string; - while ((s = strchr(s, this->regmust[0])) != 0) { + while ((s = strchr(s, this->regmust[0])) != KWSYS_NULLPTR) { if (strncmp(s, this->regmust, this->regmlen) == 0) break; // Found it. s++; } - if (s == 0) // Not present. + if (s == KWSYS_NULLPTR) // Not present. return false; } @@ -899,7 +899,7 @@ bool RegularExpression::find(char const* string, s = string; if (this->regstart != '\0') // We know what char it must start with. - while ((s = strchr(s, this->regstart)) != 0) { + while ((s = strchr(s, this->regstart)) != KWSYS_NULLPTR) { if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program)) return true; s++; @@ -933,8 +933,8 @@ int RegExpFind::regtry(const char* string, const char** start, sp1 = start; ep = end; for (i = RegularExpressionMatch::NSUBEXP; i > 0; i--) { - *sp1++ = 0; - *ep++ = 0; + *sp1++ = KWSYS_NULLPTR; + *ep++ = KWSYS_NULLPTR; } if (regmatch(prog + 1)) { start[0] = string; @@ -962,7 +962,7 @@ int RegExpFind::regmatch(const char* prog) scan = prog; - while (scan != 0) { + while (scan != KWSYS_NULLPTR) { next = regnext(scan); @@ -994,12 +994,14 @@ int RegExpFind::regmatch(const char* prog) reginput += len; } break; case ANYOF: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == 0) + if (*reginput == '\0' || + strchr(OPERAND(scan), *reginput) == KWSYS_NULLPTR) return (0); reginput++; break; case ANYBUT: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != 0) + if (*reginput == '\0' || + strchr(OPERAND(scan), *reginput) != KWSYS_NULLPTR) return (0); reginput++; break; @@ -1028,7 +1030,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set startp if some later invocation of the // same parentheses already has. // - if (regstartp[no] == 0) + if (regstartp[no] == KWSYS_NULLPTR) regstartp[no] = save; return (1); } else @@ -1056,7 +1058,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set endp if some later invocation of the // same parentheses already has. // - if (regendp[no] == 0) + if (regendp[no] == KWSYS_NULLPTR) regendp[no] = save; return (1); } else @@ -1076,7 +1078,7 @@ int RegExpFind::regmatch(const char* prog) return (1); reginput = save; scan = regnext(scan); - } while (scan != 0 && OP(scan) == BRANCH); + } while (scan != KWSYS_NULLPTR && OP(scan) == BRANCH); return (0); // NOTREACHED } @@ -1154,13 +1156,13 @@ int RegExpFind::regrepeat(const char* p) } break; case ANYOF: - while (*scan != '\0' && strchr(opnd, *scan) != 0) { + while (*scan != '\0' && strchr(opnd, *scan) != KWSYS_NULLPTR) { count++; scan++; } break; case ANYBUT: - while (*scan != '\0' && strchr(opnd, *scan) == 0) { + while (*scan != '\0' && strchr(opnd, *scan) == KWSYS_NULLPTR) { count++; scan++; } @@ -1182,11 +1184,11 @@ static const char* regnext(const char* p) int offset; if (p == regdummyptr) - return (0); + return (KWSYS_NULLPTR); offset = NEXT(p); if (offset == 0) - return (0); + return (KWSYS_NULLPTR); if (OP(p) == BACK) return (p - offset); @@ -1199,11 +1201,11 @@ static char* regnext(char* p) int offset; if (p == regdummyptr) - return (0); + return (KWSYS_NULLPTR); offset = NEXT(p); if (offset == 0) - return (0); + return (KWSYS_NULLPTR); if (OP(p) == BACK) return (p - offset); diff --git a/Source/kwsys/RegularExpression.hxx.in b/Source/kwsys/RegularExpression.hxx.in index a3fe72d..3cbbeb8 100644 --- a/Source/kwsys/RegularExpression.hxx.in +++ b/Source/kwsys/RegularExpression.hxx.in @@ -202,7 +202,7 @@ inline std::string RegularExpressionMatch::match(int n) const * * ? Matches preceding pattern zero or once only * - * () Saves a matched expression and uses it in a later match + * () Saves a matched expression and uses it in a later match * * Note that more than one of these metacharacters can be used * in a single regular expression in order to create complex diff --git a/Source/kwsys/SharedForward.h.in b/Source/kwsys/SharedForward.h.in index f638267..0caf5e7 100644 --- a/Source/kwsys/SharedForward.h.in +++ b/Source/kwsys/SharedForward.h.in @@ -225,7 +225,7 @@ static const char kwsys_shared_forward_path_slash[2] = { #define KWSYS_SHARED_FORWARD_LDD_N 1 #define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" -/* OSX */ +/* OS X */ #elif defined(__APPLE__) #define KWSYS_SHARED_FORWARD_LDD "otool", "-L" #define KWSYS_SHARED_FORWARD_LDD_N 2 diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index ab1f40a..7503469 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -340,7 +340,7 @@ public: bool DoesCPUSupportCPUID(); - // Retrieve memory information in megabyte. + // Retrieve memory information in MiB. size_t GetTotalVirtualMemory(); size_t GetAvailableVirtualMemory(); size_t GetTotalPhysicalMemory(); @@ -348,7 +348,7 @@ public: LongLong GetProcessId(); - // Retrieve memory information in kib + // Retrieve memory information in KiB. LongLong GetHostMemoryTotal(); LongLong GetHostMemoryAvailable(const char* envVarName); LongLong GetHostMemoryUsed(); @@ -736,7 +736,7 @@ bool SystemInformation::DoesCPUSupportCPUID() return this->Implementation->DoesCPUSupportCPUID(); } -// Retrieve memory information in megabyte. +// Retrieve memory information in MiB. size_t SystemInformation::GetTotalVirtualMemory() { return this->Implementation->GetTotalVirtualMemory(); @@ -881,7 +881,7 @@ int LoadLines(FILE* file, std::vector<std::string>& lines) char buf[bufSize] = { '\0' }; while (!feof(file) && !ferror(file)) { errno = 0; - if (fgets(buf, bufSize, file) == 0) { + if (fgets(buf, bufSize, file) == KWSYS_NULLPTR) { if (ferror(file) && (errno == EINTR)) { clearerr(file); } @@ -977,7 +977,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, T* values) { FILE* file = popen(command, "r"); - if (file == 0) { + if (file == KWSYS_NULLPTR) { return -1; } std::vector<std::string> fields; @@ -987,7 +987,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, return -1; } int i = 0; - while (fieldNames[i] != NULL) { + while (fieldNames[i] != KWSYS_NULLPTR) { int ierr = NameValue(fields, fieldNames[i], values[i]); if (ierr) { return -(i + 2); @@ -1023,7 +1023,8 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGFPE: - oss << "Caught SIGFPE at " << (sigInfo->si_addr == 0 ? "0x" : "") + oss << "Caught SIGFPE at " + << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { #if defined(FPE_INTDIV) @@ -1071,7 +1072,8 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGSEGV: - oss << "Caught SIGSEGV at " << (sigInfo->si_addr == 0 ? "0x" : "") + oss << "Caught SIGSEGV at " + << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case SEGV_MAPERR: @@ -1089,7 +1091,8 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGBUS: - oss << "Caught SIGBUS at " << (sigInfo->si_addr == 0 ? "0x" : "") + oss << "Caught SIGBUS at " + << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case BUS_ADRALN: @@ -1129,7 +1132,8 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGILL: - oss << "Caught SIGILL at " << (sigInfo->si_addr == 0 ? "0x" : "") + oss << "Caught SIGILL at " + << (sigInfo->si_addr == KWSYS_NULLPTR ? "0x" : "") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case ILL_ILLOPC: @@ -1313,8 +1317,8 @@ SymbolProperties::SymbolProperties() // not using an initializer list // to avoid some PGI compiler warnings this->SetBinary("???"); - this->SetBinaryBaseAddress(NULL); - this->Address = NULL; + this->SetBinaryBaseAddress(KWSYS_NULLPTR); + this->Address = KWSYS_NULLPTR; this->SetSourceFile("???"); this->SetFunction("???"); this->SetLineNumber(-1); @@ -1649,7 +1653,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( // any number of interfaces on this system we look for the // first of these that contains the name returned by gethostname // and is longer. failing that we return gethostname and indicate - // with a failure code. Return of a failure code is not necessarilly + // with a failure code. Return of a failure code is not necessarily // an indication of an error. for instance gethostname may return // the fully qualified domain name, or there may not be one if the // system lives on a private network such as in the case of a cluster @@ -1671,7 +1675,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( return -2; } - for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) { + for (ifa = ifas; ifa != KWSYS_NULLPTR; ifa = ifa->ifa_next) { int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1; // Skip Loopback interfaces if (((fam == AF_INET) || (fam == AF_INET6)) && @@ -1682,7 +1686,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( : sizeof(struct sockaddr_in6)); ierr = getnameinfo(ifa->ifa_addr, static_cast<socklen_t>(addrlen), host, - NI_MAXHOST, NULL, 0, NI_NAMEREQD); + NI_MAXHOST, KWSYS_NULLPTR, 0, NI_NAMEREQD); if (ierr) { // don't report the failure now since we may succeed on another // interface. If all attempts fail then return the failure code. @@ -3609,7 +3613,7 @@ SystemInformationImplementation::GetHostMemoryTotal() #elif defined(__APPLE__) uint64_t mem; size_t len = sizeof(mem); - int ierr = sysctlbyname("hw.memsize", &mem, &len, NULL, 0); + int ierr = sysctlbyname("hw.memsize", &mem, &len, KWSYS_NULLPTR, 0); if (ierr) { return -1; } @@ -3752,7 +3756,8 @@ SystemInformationImplementation::GetHostMemoryUsed() if (psz < 1) { return -1; } - const char* names[3] = { "Pages wired down:", "Pages active:", NULL }; + const char* names[3] = { "Pages wired down:", "Pages active:", + KWSYS_NULLPTR }; SystemInformation::LongLong values[2] = { SystemInformation::LongLong(0) }; int ierr = GetFieldsFromCommand("vm_stat", names, values); if (ierr) { @@ -3800,7 +3805,7 @@ SystemInformationImplementation::GetProcMemoryUsed() std::ostringstream oss; oss << "ps -o rss= -p " << pid; FILE* file = popen(oss.str().c_str(), "r"); - if (file == 0) { + if (file == KWSYS_NULLPTR) { return -1; } oss.str(""); @@ -3933,13 +3938,13 @@ void SystemInformationImplementation::SetStackTraceOnError(int enable) if (enable && !saOrigValid) { // save the current actions - sigaction(SIGABRT, 0, &saABRTOrig); - sigaction(SIGSEGV, 0, &saSEGVOrig); - sigaction(SIGTERM, 0, &saTERMOrig); - sigaction(SIGINT, 0, &saINTOrig); - sigaction(SIGILL, 0, &saILLOrig); - sigaction(SIGBUS, 0, &saBUSOrig); - sigaction(SIGFPE, 0, &saFPEOrig); + sigaction(SIGABRT, KWSYS_NULLPTR, &saABRTOrig); + sigaction(SIGSEGV, KWSYS_NULLPTR, &saSEGVOrig); + sigaction(SIGTERM, KWSYS_NULLPTR, &saTERMOrig); + sigaction(SIGINT, KWSYS_NULLPTR, &saINTOrig); + sigaction(SIGILL, KWSYS_NULLPTR, &saILLOrig); + sigaction(SIGBUS, KWSYS_NULLPTR, &saBUSOrig); + sigaction(SIGFPE, KWSYS_NULLPTR, &saFPEOrig); // enable read, disable write saOrigValid = 1; @@ -3953,22 +3958,22 @@ void SystemInformationImplementation::SetStackTraceOnError(int enable) #endif sigemptyset(&sa.sa_mask); - sigaction(SIGABRT, &sa, 0); - sigaction(SIGSEGV, &sa, 0); - sigaction(SIGTERM, &sa, 0); - sigaction(SIGINT, &sa, 0); - sigaction(SIGILL, &sa, 0); - sigaction(SIGBUS, &sa, 0); - sigaction(SIGFPE, &sa, 0); + sigaction(SIGABRT, &sa, KWSYS_NULLPTR); + sigaction(SIGSEGV, &sa, KWSYS_NULLPTR); + sigaction(SIGTERM, &sa, KWSYS_NULLPTR); + sigaction(SIGINT, &sa, KWSYS_NULLPTR); + sigaction(SIGILL, &sa, KWSYS_NULLPTR); + sigaction(SIGBUS, &sa, KWSYS_NULLPTR); + sigaction(SIGFPE, &sa, KWSYS_NULLPTR); } else if (!enable && saOrigValid) { // restore previous actions - sigaction(SIGABRT, &saABRTOrig, 0); - sigaction(SIGSEGV, &saSEGVOrig, 0); - sigaction(SIGTERM, &saTERMOrig, 0); - sigaction(SIGINT, &saINTOrig, 0); - sigaction(SIGILL, &saILLOrig, 0); - sigaction(SIGBUS, &saBUSOrig, 0); - sigaction(SIGFPE, &saFPEOrig, 0); + sigaction(SIGABRT, &saABRTOrig, KWSYS_NULLPTR); + sigaction(SIGSEGV, &saSEGVOrig, KWSYS_NULLPTR); + sigaction(SIGTERM, &saTERMOrig, KWSYS_NULLPTR); + sigaction(SIGINT, &saINTOrig, KWSYS_NULLPTR); + sigaction(SIGILL, &saILLOrig, KWSYS_NULLPTR); + sigaction(SIGBUS, &saBUSOrig, KWSYS_NULLPTR); + sigaction(SIGFPE, &saFPEOrig, KWSYS_NULLPTR); // enable write, disable read saOrigValid = 0; @@ -4404,7 +4409,7 @@ bool SystemInformationImplementation::ParseSysCtl() int err = 0; uint64_t value = 0; size_t len = sizeof(value); - sysctlbyname("hw.memsize", &value, &len, NULL, 0); + sysctlbyname("hw.memsize", &value, &len, KWSYS_NULLPTR, 0); this->TotalPhysicalMemory = static_cast<size_t>(value / 1048576); // Parse values for Mac @@ -4414,7 +4419,7 @@ bool SystemInformationImplementation::ParseSysCtl() if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) == KERN_SUCCESS) { len = sizeof(value); - err = sysctlbyname("hw.pagesize", &value, &len, NULL, 0); + err = sysctlbyname("hw.pagesize", &value, &len, KWSYS_NULLPTR, 0); int64_t available_memory = vmstat.free_count * value; this->AvailablePhysicalMemory = static_cast<size_t>(available_memory / 1048576); @@ -4426,7 +4431,7 @@ bool SystemInformationImplementation::ParseSysCtl() size_t miblen = sizeof(mib) / sizeof(mib[0]); struct xsw_usage swap; len = sizeof(swap); - err = sysctl(mib, miblen, &swap, &len, NULL, 0); + err = sysctl(mib, miblen, &swap, &len, KWSYS_NULLPTR, 0); if (err == 0) { this->AvailableVirtualMemory = static_cast<size_t>(swap.xsu_avail / 1048576); @@ -4439,71 +4444,75 @@ bool SystemInformationImplementation::ParseSysCtl() // CPU Info len = sizeof(this->NumberOfPhysicalCPU); - sysctlbyname("hw.physicalcpu", &this->NumberOfPhysicalCPU, &len, NULL, 0); + sysctlbyname("hw.physicalcpu", &this->NumberOfPhysicalCPU, &len, + KWSYS_NULLPTR, 0); len = sizeof(this->NumberOfLogicalCPU); - sysctlbyname("hw.logicalcpu", &this->NumberOfLogicalCPU, &len, NULL, 0); + sysctlbyname("hw.logicalcpu", &this->NumberOfLogicalCPU, &len, KWSYS_NULLPTR, + 0); int cores_per_package = 0; len = sizeof(cores_per_package); err = sysctlbyname("machdep.cpu.cores_per_package", &cores_per_package, &len, - NULL, 0); + KWSYS_NULLPTR, 0); // That name was not found, default to 1 this->Features.ExtendedFeatures.LogicalProcessorsPerPhysical = err != 0 ? 1 : static_cast<unsigned char>(cores_per_package); len = sizeof(value); - sysctlbyname("hw.cpufrequency", &value, &len, NULL, 0); + sysctlbyname("hw.cpufrequency", &value, &len, KWSYS_NULLPTR, 0); this->CPUSpeedInMHz = static_cast<float>(value) / 1000000; // Chip family len = sizeof(this->ChipID.Family); // Seems only the intel chips will have this name so if this fails it is // probably a PPC machine - err = - sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, NULL, 0); + err = sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, + KWSYS_NULLPTR, 0); if (err != 0) // Go back to names we know but are less descriptive { this->ChipID.Family = 0; ::memset(retBuf, 0, 128); len = 32; - err = sysctlbyname("hw.machine", &retBuf, &len, NULL, 0); + err = sysctlbyname("hw.machine", &retBuf, &len, KWSYS_NULLPTR, 0); std::string machineBuf(retBuf); if (machineBuf.find_first_of("Power") != std::string::npos) { this->ChipID.Vendor = "IBM"; len = sizeof(this->ChipID.Family); - err = sysctlbyname("hw.cputype", &this->ChipID.Family, &len, NULL, 0); + err = sysctlbyname("hw.cputype", &this->ChipID.Family, &len, + KWSYS_NULLPTR, 0); len = sizeof(this->ChipID.Model); - err = sysctlbyname("hw.cpusubtype", &this->ChipID.Model, &len, NULL, 0); + err = sysctlbyname("hw.cpusubtype", &this->ChipID.Model, &len, + KWSYS_NULLPTR, 0); this->FindManufacturer(); } } else // Should be an Intel Chip. { len = sizeof(this->ChipID.Family); - err = - sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, NULL, 0); + err = sysctlbyname("machdep.cpu.family", &this->ChipID.Family, &len, + KWSYS_NULLPTR, 0); ::memset(retBuf, 0, 128); len = 128; - err = sysctlbyname("machdep.cpu.vendor", retBuf, &len, NULL, 0); + err = sysctlbyname("machdep.cpu.vendor", retBuf, &len, KWSYS_NULLPTR, 0); // Chip Vendor this->ChipID.Vendor = retBuf; this->FindManufacturer(); // Chip Model len = sizeof(value); - err = sysctlbyname("machdep.cpu.model", &value, &len, NULL, 0); + err = sysctlbyname("machdep.cpu.model", &value, &len, KWSYS_NULLPTR, 0); this->ChipID.Model = static_cast<int>(value); // Chip Stepping len = sizeof(value); value = 0; - err = sysctlbyname("machdep.cpu.stepping", &value, &len, NULL, 0); + err = sysctlbyname("machdep.cpu.stepping", &value, &len, KWSYS_NULLPTR, 0); if (!err) { this->ChipID.Revision = static_cast<int>(value); } // feature string - char* buf = 0; + char* buf = KWSYS_NULLPTR; size_t allocSize = 128; err = 0; @@ -4520,7 +4529,8 @@ bool SystemInformationImplementation::ParseSysCtl() } buf[0] = ' '; len = allocSize - 2; // keep space for leading and trailing space - err = sysctlbyname("machdep.cpu.features", buf + 1, &len, NULL, 0); + err = + sysctlbyname("machdep.cpu.features", buf + 1, &len, KWSYS_NULLPTR, 0); } if (!err && buf && len) { // now we can match every flags as space + flag + space @@ -4561,7 +4571,8 @@ bool SystemInformationImplementation::ParseSysCtl() // brand string ::memset(retBuf, 0, sizeof(retBuf)); len = sizeof(retBuf); - err = sysctlbyname("machdep.cpu.brand_string", retBuf, &len, NULL, 0); + err = + sysctlbyname("machdep.cpu.brand_string", retBuf, &len, KWSYS_NULLPTR, 0); if (!err) { this->ChipID.ProcessorName = retBuf; this->ChipID.ModelName = retBuf; @@ -4569,10 +4580,10 @@ bool SystemInformationImplementation::ParseSysCtl() // Cache size len = sizeof(value); - err = sysctlbyname("hw.l1icachesize", &value, &len, NULL, 0); + err = sysctlbyname("hw.l1icachesize", &value, &len, KWSYS_NULLPTR, 0); this->Features.L1CacheSize = static_cast<int>(value); len = sizeof(value); - err = sysctlbyname("hw.l2cachesize", &value, &len, NULL, 0); + err = sysctlbyname("hw.l2cachesize", &value, &len, KWSYS_NULLPTR, 0); this->Features.L2CacheSize = static_cast<int>(value); return true; @@ -4609,7 +4620,7 @@ std::string SystemInformationImplementation::RunProcess( kwsysProcess_Execute(gp); - char* data = NULL; + char* data = KWSYS_NULLPTR; int length; double timeout = 255; int pipe; // pipe id as returned by kwsysProcess_WaitForData() @@ -4621,7 +4632,7 @@ std::string SystemInformationImplementation::RunProcess( { buffer.append(data, length); } - kwsysProcess_WaitForExit(gp, 0); + kwsysProcess_WaitForExit(gp, KWSYS_NULLPTR); int result = 0; switch (kwsysProcess_GetState(gp)) { @@ -4694,7 +4705,7 @@ std::string SystemInformationImplementation::ParseValueFromKStat( std::string lastArg = command.substr(start + 1, command.size() - start - 1); args.push_back(lastArg.c_str()); - args.push_back(0); + args.push_back(KWSYS_NULLPTR); std::string buffer = this->RunProcess(args); @@ -5379,7 +5390,7 @@ int SystemInformationImplementation::CallSwVers(const char* arg, std::vector<const char*> args; args.push_back("sw_vers"); args.push_back(arg); - args.push_back(0); + args.push_back(KWSYS_NULLPTR); ver = this->RunProcess(args); this->TrimNewline(ver); #else diff --git a/Source/kwsys/SystemInformation.hxx.in b/Source/kwsys/SystemInformation.hxx.in index 5678e8a..fe7e8b5 100644 --- a/Source/kwsys/SystemInformation.hxx.in +++ b/Source/kwsys/SystemInformation.hxx.in @@ -103,14 +103,14 @@ public: // Retrieve id of the current running process LongLong GetProcessId(); - // Retrieve memory information in megabyte. + // Retrieve memory information in MiB. size_t GetTotalVirtualMemory(); size_t GetAvailableVirtualMemory(); size_t GetTotalPhysicalMemory(); size_t GetAvailablePhysicalMemory(); // returns an informative general description if the installed and - // available ram on this system. See the GetHostMmeoryTotal, and + // available ram on this system. See the GetHostMemoryTotal, and // Get{Host,Proc}MemoryAvailable methods for more information. std::string GetMemoryDescription(const char* hostLimitEnvVarName = NULL, const char* procLimitEnvVarName = NULL); diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index f547362..999beb3 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -107,7 +107,8 @@ extern char** environ; // getpwnam doesn't exist on Windows and Cray Xt3/Catamount // same for TIOCGWINSZ -#if defined(_WIN32) || defined(__LIBCATAMOUNT__) +#if defined(_WIN32) || defined(__LIBCATAMOUNT__) || \ + (defined(HAVE_GETPWNAM) && HAVE_GETPWNAM == 0) #undef HAVE_GETPWNAM #undef HAVE_TTY_INFO #else @@ -306,7 +307,7 @@ inline int Chdir(const std::string& dir) return chdir(dir.c_str()); } inline void Realpath(const std::string& path, std::string& resolved_path, - std::string* errorMessage = 0) + std::string* errorMessage = KWSYS_NULLPTR) { char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH]; @@ -352,7 +353,7 @@ double SystemTools::GetTime(void) 11644473600.0); #else struct timeval t; - gettimeofday(&t, 0); + gettimeofday(&t, KWSYS_NULLPTR); return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec); #endif } @@ -414,7 +415,7 @@ public: const envchar* Release(const envchar* env) { - const envchar* old = 0; + const envchar* old = KWSYS_NULLPTR; iterator i = this->find(env); if (i != this->end()) { old = *i; @@ -489,7 +490,7 @@ void SystemTools::GetPath(std::vector<std::string>& path, const char* env) const char* SystemTools::GetEnvImpl(const char* key) { - const char* v = 0; + const char* v = KWSYS_NULLPTR; #if defined(_WIN32) std::string env; if (SystemTools::GetEnv(key, env)) { @@ -545,7 +546,7 @@ bool SystemTools::HasEnv(const char* key) #else const char* v = getenv(key); #endif - return v != 0; + return v != KWSYS_NULLPTR; } bool SystemTools::HasEnv(const std::string& key) @@ -776,7 +777,7 @@ bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode) while ((pos = dir.find('/', pos)) != std::string::npos) { topdir = dir.substr(0, pos); - if (Mkdir(topdir) == 0 && mode != 0) { + if (Mkdir(topdir) == 0 && mode != KWSYS_NULLPTR) { SystemTools::SetPermissions(topdir, *mode); } @@ -795,7 +796,7 @@ bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode) ) { return false; } - } else if (mode != 0) { + } else if (mode != KWSYS_NULLPTR) { SystemTools::SetPermissions(topdir, *mode); } @@ -1072,7 +1073,7 @@ bool SystemTools::WriteRegistryValue(const std::string&, const std::string&, // HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath // => will delete the data of the "default" value of the key // HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root -// => will delete the data of the "Root" value of the key +// => will delete the data of the "Root" value of the key #if defined(_WIN32) && !defined(__CYGWIN__) bool SystemTools::DeleteRegistryValue(const std::string& key, KeyWOW64 view) @@ -1520,7 +1521,7 @@ char* SystemTools::AppendStrings(const char* str1, const char* str2) size_t len1 = strlen(str1); char* newstr = new char[len1 + strlen(str2) + 1]; if (!newstr) { - return 0; + return KWSYS_NULLPTR; } strcpy(newstr, str1); strcat(newstr + len1, str2); @@ -1543,7 +1544,7 @@ char* SystemTools::AppendStrings(const char* str1, const char* str2, size_t len1 = strlen(str1), len2 = strlen(str2); char* newstr = new char[len1 + len2 + strlen(str3) + 1]; if (!newstr) { - return 0; + return KWSYS_NULLPTR; } strcpy(newstr, str1); strcat(newstr + len1, str2); @@ -1593,7 +1594,7 @@ size_t SystemTools::CountChar(const char* str, char c) char* SystemTools::RemoveChars(const char* str, const char* toremove) { if (!str) { - return NULL; + return KWSYS_NULLPTR; } char* clean_str = new char[strlen(str) + 1]; char* ptr = clean_str; @@ -1615,7 +1616,7 @@ char* SystemTools::RemoveChars(const char* str, const char* toremove) char* SystemTools::RemoveCharsButUpperHex(const char* str) { if (!str) { - return 0; + return KWSYS_NULLPTR; } char* clean_str = new char[strlen(str) + 1]; char* ptr = clean_str; @@ -1696,7 +1697,7 @@ bool SystemTools::StringEndsWith(const std::string& str1, const char* str2) const char* SystemTools::FindLastString(const char* str1, const char* str2) { if (!str1 || !str2) { - return NULL; + return KWSYS_NULLPTR; } size_t len1 = strlen(str1), len2 = strlen(str2); @@ -1709,7 +1710,7 @@ const char* SystemTools::FindLastString(const char* str1, const char* str2) } while (ptr-- != str1); } - return NULL; + return KWSYS_NULLPTR; } // Duplicate string @@ -1719,7 +1720,7 @@ char* SystemTools::DuplicateString(const char* str) char* newstr = new char[strlen(str) + 1]; return strcpy(newstr, str); } - return NULL; + return KWSYS_NULLPTR; } // Return a cropped string @@ -3100,7 +3101,7 @@ bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, std::string SystemTools::CollapseFullPath(const std::string& in_relative) { - return SystemTools::CollapseFullPath(in_relative, 0); + return SystemTools::CollapseFullPath(in_relative, KWSYS_NULLPTR); } void SystemTools::AddTranslationPath(const std::string& a, @@ -3326,7 +3327,7 @@ std::string SystemTools::RelativePath(const std::string& local, unsigned int sameCount = 0; while (((sameCount <= (localSplit.size() - 1)) && (sameCount <= (remoteSplit.size() - 1))) && -// for windows and apple do a case insensitive string compare +// for Windows and Apple do a case insensitive string compare #if defined(_WIN32) || defined(__APPLE__) SystemTools::Strucmp(localSplit[sameCount].c_str(), remoteSplit[sameCount].c_str()) == 0 diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 8a02b75..719d45c 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -881,7 +881,7 @@ public: /** * Get the width of the terminal window. The code may or may not work, so - * make sure you have some resonable defaults prepared if the code returns + * make sure you have some reasonable defaults prepared if the code returns * some bogus size. */ static int GetTerminalWidth(); diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx index d2215d6..78baed9 100644 --- a/Source/kwsys/testCommandLineArguments.cxx +++ b/Source/kwsys/testCommandLineArguments.cxx @@ -76,7 +76,7 @@ int testCommandLineArguments(int argc, char* argv[]) int some_int_variable = 10; double some_double_variable = 10.10; - char* some_string_variable = 0; + char* some_string_variable = KWSYS_NULLPTR; std::string some_stl_string_variable = ""; bool some_bool_variable = false; bool some_bool_variable1 = false; @@ -202,7 +202,7 @@ int testCommandLineArguments(int argc, char* argv[]) for (cc = 0; cc < strings_argument.size(); ++cc) { delete[] strings_argument[cc]; - strings_argument[cc] = 0; + strings_argument[cc] = KWSYS_NULLPTR; } return res; } diff --git a/Source/kwsys/testCommandLineArguments1.cxx b/Source/kwsys/testCommandLineArguments1.cxx index 5a03401..5dfa5e3 100644 --- a/Source/kwsys/testCommandLineArguments1.cxx +++ b/Source/kwsys/testCommandLineArguments1.cxx @@ -21,7 +21,7 @@ int testCommandLineArguments1(int argc, char* argv[]) arg.Initialize(argc, argv); int n = 0; - char* m = 0; + char* m = KWSYS_NULLPTR; std::string p; int res = 0; @@ -55,11 +55,11 @@ int testCommandLineArguments1(int argc, char* argv[]) delete[] m; } - char** newArgv = 0; + char** newArgv = KWSYS_NULLPTR; int newArgc = 0; arg.GetUnusedArguments(&newArgc, &newArgv); int cc; - const char* valid_unused_args[9] = { 0, + const char* valid_unused_args[9] = { KWSYS_NULLPTR, "--ignored", "--second-ignored", "third-ignored", diff --git a/Source/kwsys/testEncoding.cxx b/Source/kwsys/testEncoding.cxx index 2742fe4..6bf273f 100644 --- a/Source/kwsys/testEncoding.cxx +++ b/Source/kwsys/testEncoding.cxx @@ -84,7 +84,7 @@ static int testRobustEncoding() // this conversion could fail std::wstring wstr = kwsys::Encoding::ToWide(cstr); - wstr = kwsys::Encoding::ToWide(NULL); + wstr = kwsys::Encoding::ToWide(KWSYS_NULLPTR); if (wstr != L"") { const wchar_t* wcstr = wstr.c_str(); std::cout << "ToWide(NULL) returned"; @@ -112,7 +112,7 @@ static int testRobustEncoding() std::string win_str = kwsys::Encoding::ToNarrow(cwstr); #endif - std::string str = kwsys::Encoding::ToNarrow(NULL); + std::string str = kwsys::Encoding::ToNarrow(KWSYS_NULLPTR); if (str != "") { std::cout << "ToNarrow(NULL) returned " << str << std::endl; ret++; diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index a6af9cc..f1321e5 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx @@ -52,7 +52,7 @@ static const char* toUnixPaths[][2] = { { "\\\\usr\\local\\bin\\passwd", "//usr/local/bin/passwd" }, { "\\\\usr\\lo cal\\bin\\pa sswd", "//usr/lo cal/bin/pa sswd" }, { "\\\\usr\\lo\\ cal\\bin\\pa\\ sswd", "//usr/lo\\ cal/bin/pa\\ sswd" }, - { 0, 0 } + { KWSYS_NULLPTR, KWSYS_NULLPTR } }; static bool CheckConvertToUnixSlashes(std::string const& input, @@ -68,10 +68,11 @@ static bool CheckConvertToUnixSlashes(std::string const& input, return true; } -static const char* checkEscapeChars[][4] = { { "1 foo 2 bar 2", "12", "\\", - "\\1 foo \\2 bar \\2" }, - { " {} ", "{}", "#", " #{#} " }, - { 0, 0, 0, 0 } }; +static const char* checkEscapeChars[][4] = { + { "1 foo 2 bar 2", "12", "\\", "\\1 foo \\2 bar \\2" }, + { " {} ", "{}", "#", " #{#} " }, + { KWSYS_NULLPTR, KWSYS_NULLPTR, KWSYS_NULLPTR, KWSYS_NULLPTR } +}; static bool CheckEscapeChars(std::string const& input, const char* chars_to_escape, char escape_char, @@ -159,7 +160,7 @@ static bool CheckFileOperations() res = false; } // calling with 0 pointer should return false - if (kwsys::SystemTools::MakeDirectory(0)) { + if (kwsys::SystemTools::MakeDirectory(KWSYS_NULLPTR)) { std::cerr << "Problem with MakeDirectory(0)" << std::endl; res = false; } @@ -217,11 +218,11 @@ static bool CheckFileOperations() } // calling with 0 pointer should return false - if (kwsys::SystemTools::FileExists(0)) { + if (kwsys::SystemTools::FileExists(KWSYS_NULLPTR)) { std::cerr << "Problem with FileExists(0)" << std::endl; res = false; } - if (kwsys::SystemTools::FileExists(0, true)) { + if (kwsys::SystemTools::FileExists(KWSYS_NULLPTR, true)) { std::cerr << "Problem with FileExists(0) as file" << std::endl; res = false; } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index fb7313f..08bfebe 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -390,6 +390,9 @@ if(BUILD_TESTING) endif() ADD_TEST_MACRO(SourcesProperty SourcesProperty) ADD_TEST_MACRO(SourceFileProperty SourceFileProperty) + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + ADD_TEST_MACRO(SourceFileIncludeDirProperty SourceFileIncludeDirProperty) + endif() if(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) set(runCxxDialectTest 1) @@ -624,7 +627,6 @@ if(BUILD_TESTING) "CodeLite" "Eclipse CDT4" "Kate" - "KDevelop3" "Sublime Text 2") foreach(extraGenerator ${extraGenerators}) @@ -3195,36 +3197,58 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release --test-command ${JAVA_RUNTIME} -classpath hello3.jar HelloWorld) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJarSourceListAndOutput") - # For next test, java tool must have same architecture as toolchain + # For next tests, java tool must have same architecture as toolchain math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8") execute_process( COMMAND "${Java_JAVA_EXECUTABLE}" -d${_object_mode} -version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE _result ) if(_result EQUAL 0) - if(_isMultiConfig) - set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah/$<CONFIGURATION>) - else() - set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah) + ## next test is valid only if Java version is less than 1.10 + if ("${Java_VERSION}" VERSION_LESS 1.10) + if(_isMultiConfig) + set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah/$<CONFIGURATION>) + else() + set (JAVAH_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaJavah) + endif() + add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/JavaJavah" + "${CMake_BINARY_DIR}/Tests/JavaJavah" + ${build_generator_args} + --build-project helloJavah + --build-two-config + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/" + --build-options ${build_options} + --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVAH_LIBRARY_PATH} -classpath hello3.jar HelloWorld2) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah") + endif() + ## next test is valid only if Java is, at least, version 1.8 + if (NOT "${Java_VERSION}" VERSION_LESS 1.8) + if(_isMultiConfig) + set (JAVANATIVEHEADERS_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/$<CONFIGURATION>) + else() + set (JAVANATIVEHEADERS_LIBRARY_PATH ${CMake_BINARY_DIR}/Tests/JavaNativeHeaders) + endif() + add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders" + "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders" + ${build_generator_args} + --build-project helloJavaNativeHeaders + --build-two-config + --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/" + --build-options ${build_options} + --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVANATIVEHEADERS_LIBRARY_PATH} -classpath hello4.jar HelloWorld3) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders") endif() - add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/JavaJavah" - "${CMake_BINARY_DIR}/Tests/JavaJavah" - ${build_generator_args} - --build-project helloJavah - --build-two-config - --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/" - --build-options ${build_options} - --test-command ${JAVA_RUNTIME} -Djava.library.path=${JAVAH_LIBRARY_PATH} -classpath hello3.jar HelloWorld2) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah") endif() endif() endif() endif() # add some cross compiler tests, for now only with makefile based generators - if(CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "KDevelop") + if(CMAKE_GENERATOR MATCHES "Makefiles") # if sdcc is found, build the SimpleCOnly project with sdcc find_program(SDCC_EXECUTABLE sdcc) diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 4586357..19d12e5 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -260,17 +260,49 @@ add_custom_target(check-part4 ALL #----------------------------------------------------------------------------- # Cover source file properties with generator expressions. -add_executable(srcgenex_flags srcgenex_flags.c) -set_property(SOURCE srcgenex_flags.c PROPERTY COMPILE_FLAGS "-DNAME=$<TARGET_PROPERTY:NAME>") - -add_executable(srcgenex_flags_COMPILE_LANGUAGE srcgenex_flags_COMPILE_LANGUAGE.c) -set_property(SOURCE srcgenex_flags_COMPILE_LANGUAGE.c PROPERTY COMPILE_FLAGS "$<$<COMPILE_LANGUAGE:C>:-DNAME=$<TARGET_PROPERTY:NAME>>") - -add_executable(srcgenex_defs srcgenex_defs.c) -set_property(SOURCE srcgenex_defs.c PROPERTY COMPILE_DEFINITIONS NAME=$<TARGET_PROPERTY:NAME>) - -add_executable(srcgenex_defs_COMPILE_LANGUAGE srcgenex_defs_COMPILE_LANGUAGE.c) -set_property(SOURCE srcgenex_defs_COMPILE_LANGUAGE.c PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) +## generate various source files +foreach (item IN ITEMS flags flags_COMPILE_LANGUAGE + options options_COMPILE_LANGUAGE + defs defs_COMPILE_LANGUAGE) + set(TARGET_NAME srcgenex_${item}) + configure_file(srcgenex.c.in ${TARGET_NAME}.c @ONLY) +endforeach() +add_executable(srcgenex_flags "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_flags.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_flags.c" + PROPERTY COMPILE_FLAGS "-DNAME=$<TARGET_PROPERTY:NAME>") +add_executable(srcgenex_flags_COMPILE_LANGUAGE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_flags_COMPILE_LANGUAGE.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_flags_COMPILE_LANGUAGE.c" + PROPERTY COMPILE_FLAGS "$<$<COMPILE_LANGUAGE:C>:-DNAME=$<TARGET_PROPERTY:NAME>>") + +add_executable(srcgenex_options "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_options.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_options.c" + PROPERTY COMPILE_OPTIONS -DUNUSED -DNAME=$<TARGET_PROPERTY:NAME>) +add_executable(srcgenex_options_COMPILE_LANGUAGE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_options_COMPILE_LANGUAGE.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_options_COMPILE_LANGUAGE.c" + PROPERTY COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:C>:-DNAME=$<TARGET_PROPERTY:NAME>>) + +add_executable(srcgenex_defs "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_defs.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_defs.c" + PROPERTY COMPILE_DEFINITIONS UNUSED NAME=$<TARGET_PROPERTY:NAME>) +add_executable(srcgenex_defs_COMPILE_LANGUAGE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_defs_COMPILE_LANGUAGE.c") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_defs_COMPILE_LANGUAGE.c" + PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) + +foreach (item IN ITEMS basic COMPILE_LANGUAGE) + set(TARGET_NAME srcgenex_includes_${item}) + configure_file(srcgenex_includes.h.in "sf_includes_${item}/${TARGET_NAME}.h" @ONLY) + configure_file(srcgenex_includes.c.in ${TARGET_NAME}.c @ONLY) +endforeach() +add_executable(srcgenex_includes_basic "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_includes_basic.c") +# first include directory is useless but ensure list aspect is tested +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_includes_basic.c" + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/sf_includes_basic") +if (CMAKE_GENERATOR MATCHES "Makefiles|Ninja|Watcom WMake") + add_executable(srcgenex_includes_COMPILE_LANGUAGE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_includes_COMPILE_LANGUAGE.c") + # first include directory is useless but ensure list aspect is tested + set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/srcgenex_includes_COMPILE_LANGUAGE.c" + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}" $<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_BINARY_DIR}/sf_includes_COMPILE_LANGUAGE>) +endif() #----------------------------------------------------------------------------- # Cover test properties with generator expressions. diff --git a/Tests/GeneratorExpression/srcgenex_defs.c b/Tests/GeneratorExpression/srcgenex.c.in index 883e631..4a43bd1 100644 --- a/Tests/GeneratorExpression/srcgenex_defs.c +++ b/Tests/GeneratorExpression/srcgenex.c.in @@ -1,4 +1,4 @@ -int srcgenex_defs(void) +int @TARGET_NAME@(void) { return 0; } diff --git a/Tests/GeneratorExpression/srcgenex_defs_COMPILE_LANGUAGE.c b/Tests/GeneratorExpression/srcgenex_defs_COMPILE_LANGUAGE.c deleted file mode 100644 index 421b55b..0000000 --- a/Tests/GeneratorExpression/srcgenex_defs_COMPILE_LANGUAGE.c +++ /dev/null @@ -1,12 +0,0 @@ -int srcgenex_defs_COMPILE_LANGUAGE(void) -{ - return 0; -} - -int main(int argc, char* argv[]) -{ -#ifndef NAME -#error NAME not defined -#endif - return NAME(); -} diff --git a/Tests/GeneratorExpression/srcgenex_flags.c b/Tests/GeneratorExpression/srcgenex_flags.c deleted file mode 100644 index 3de2b12..0000000 --- a/Tests/GeneratorExpression/srcgenex_flags.c +++ /dev/null @@ -1,12 +0,0 @@ -int srcgenex_flags(void) -{ - return 0; -} - -int main(int argc, char* argv[]) -{ -#ifndef NAME -#error NAME not defined -#endif - return NAME(); -} diff --git a/Tests/GeneratorExpression/srcgenex_flags_COMPILE_LANGUAGE.c b/Tests/GeneratorExpression/srcgenex_flags_COMPILE_LANGUAGE.c deleted file mode 100644 index 4e80d6e..0000000 --- a/Tests/GeneratorExpression/srcgenex_flags_COMPILE_LANGUAGE.c +++ /dev/null @@ -1,12 +0,0 @@ -int srcgenex_flags_COMPILE_LANGUAGE(void) -{ - return 0; -} - -int main(int argc, char* argv[]) -{ -#ifndef NAME -#error NAME not defined -#endif - return NAME(); -} diff --git a/Tests/GeneratorExpression/srcgenex_includes.c.in b/Tests/GeneratorExpression/srcgenex_includes.c.in new file mode 100644 index 0000000..b48d7df --- /dev/null +++ b/Tests/GeneratorExpression/srcgenex_includes.c.in @@ -0,0 +1,12 @@ + +#include "@TARGET_NAME@.h" + +int @TARGET_NAME@(void) +{ + return 0; +} + +int main(int argc, char* argv[]) +{ + return @TARGET_NAME@(); +} diff --git a/Tests/GeneratorExpression/srcgenex_includes.h.in b/Tests/GeneratorExpression/srcgenex_includes.h.in new file mode 100644 index 0000000..2259ca6 --- /dev/null +++ b/Tests/GeneratorExpression/srcgenex_includes.h.in @@ -0,0 +1,7 @@ + +#if !defined @TARGET_NAME@_H +#define @TARGET_NAME@_H + +int @TARGET_NAME@(void); + +#endif diff --git a/Tests/JavaNativeHeaders/CMakeLists.txt b/Tests/JavaNativeHeaders/CMakeLists.txt new file mode 100644 index 0000000..7dc2679 --- /dev/null +++ b/Tests/JavaNativeHeaders/CMakeLists.txt @@ -0,0 +1,18 @@ +project(helloJavaNativeHeaders Java CXX) + +cmake_minimum_required (VERSION 2.6) +set(CMAKE_VERBOSE_MAKEFILE 1) + +find_package(Java COMPONENTS Development) +include (UseJava) + +# JNI support +find_package(JNI) + +add_jar(B1 D.java GENERATE_NATIVE_HEADERS D1-native) +add_jar(E1 E.java GENERATE_NATIVE_HEADERS E1-native) + +add_jar(hello4 HelloWorld3.java) + +add_library(D SHARED D.cpp E.cpp) +target_link_libraries (D PRIVATE D1-native E1-native) diff --git a/Tests/JavaNativeHeaders/D.cpp b/Tests/JavaNativeHeaders/D.cpp new file mode 100644 index 0000000..2a90a08 --- /dev/null +++ b/Tests/JavaNativeHeaders/D.cpp @@ -0,0 +1,10 @@ + +#include <jni.h> +#include <stdio.h> + +#include "D.h" + +JNIEXPORT void JNICALL Java_D_printName(JNIEnv*, jobject) +{ + printf("D\n"); +} diff --git a/Tests/JavaNativeHeaders/D.java b/Tests/JavaNativeHeaders/D.java new file mode 100644 index 0000000..449c0df --- /dev/null +++ b/Tests/JavaNativeHeaders/D.java @@ -0,0 +1,19 @@ +class D +{ + public D() + { + } + + public native void printName(); + + static { + try { + + System.loadLibrary("D"); + + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load.\n" + e); + System.exit(1); + } + } +} diff --git a/Tests/JavaNativeHeaders/E.cpp b/Tests/JavaNativeHeaders/E.cpp new file mode 100644 index 0000000..fb98946 --- /dev/null +++ b/Tests/JavaNativeHeaders/E.cpp @@ -0,0 +1,10 @@ + +#include <jni.h> +#include <stdio.h> + +#include "E.h" + +JNIEXPORT void JNICALL Java_E_printName(JNIEnv*, jobject) +{ + printf("E\n"); +} diff --git a/Tests/JavaNativeHeaders/E.java b/Tests/JavaNativeHeaders/E.java new file mode 100644 index 0000000..30fd95a --- /dev/null +++ b/Tests/JavaNativeHeaders/E.java @@ -0,0 +1,19 @@ +class E +{ + public E() + { + } + + public native void printName(); + + static { + try { + + System.loadLibrary("D"); + + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load.\n" + e); + System.exit(1); + } + } +} diff --git a/Tests/JavaNativeHeaders/HelloWorld3.java b/Tests/JavaNativeHeaders/HelloWorld3.java new file mode 100644 index 0000000..77f1fed --- /dev/null +++ b/Tests/JavaNativeHeaders/HelloWorld3.java @@ -0,0 +1,15 @@ +class HelloWorld3 +{ + public static void main(String args[]) + { + D d; + d = new D(); + d.printName(); + + E e; + e = new E(); + e.printName(); + + System.out.println("Hello World!"); + } +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index e440b7f..d5bd297 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -176,6 +176,7 @@ add_RunCMake_test(CompileDefinitions) add_RunCMake_test(CompileFeatures) add_RunCMake_test(PolicyScope) add_RunCMake_test(WriteCompilerDetectionHeader) +add_RunCMake_test(SourceProperties) if(NOT WIN32) add_RunCMake_test(PositionIndependentCode) endif() diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index cf81f36..ae9a84c 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake @@ -85,6 +85,7 @@ macro_add_test_library(libshared) macro_add_test_library(libstatic) add_subdirectory(nodeprecated) +add_subdirectory(includeguard) if(NOT BORLAND) add_subdirectory(c_identifier) endif() diff --git a/Tests/RunCMake/GenerateExportHeader/RunCMakeTest.cmake b/Tests/RunCMake/GenerateExportHeader/RunCMakeTest.cmake index 55625a8..18c3340 100644 --- a/Tests/RunCMake/GenerateExportHeader/RunCMakeTest.cmake +++ b/Tests/RunCMake/GenerateExportHeader/RunCMakeTest.cmake @@ -12,6 +12,8 @@ function(run_GEH) run_cmake(GEH) run_cmake_command(GEH-build ${CMAKE_COMMAND} --build . --config Debug) run_cmake_command(GEH-run ${RunCMake_TEST_BINARY_DIR}/GenerateExportHeader) + run_cmake_command(GEH-incguard-macro-run ${RunCMake_TEST_BINARY_DIR}/test_includeguard_macro) + run_cmake_command(GEH-incguard-custom-run ${RunCMake_TEST_BINARY_DIR}/test_includeguard_custom) file(STRINGS "${RunCMake_TEST_BINARY_DIR}/failure_test_targets" failure_test_targets) diff --git a/Tests/RunCMake/GenerateExportHeader/includeguard/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/includeguard/CMakeLists.txt new file mode 100644 index 0000000..514d27b --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/includeguard/CMakeLists.txt @@ -0,0 +1,19 @@ +set(libincludeguard_SRC libincludeguard.cpp) + +add_library(includeguard_macro ${libincludeguard_SRC}) +generate_export_header(includeguard_macro) + +set(EXPORT_HEADER includeguard_macro_export.h) +set(DEF_SYMBOL INCLUDEGUARD_MACRO_EXPORT_H) +set(NDEF_SYMBOL CUSTOM_GUARD) +configure_file(main.cpp.in main_macro.cpp) +add_executable(test_includeguard_macro ${CMAKE_CURRENT_BINARY_DIR}/main_macro.cpp) + +add_library(includeguard_custom ${libincludeguard_SRC}) +generate_export_header(includeguard_custom INCLUDE_GUARD_NAME CUSTOM_GUARD) + +set(EXPORT_HEADER includeguard_custom_export.h) +set(DEF_SYMBOL CUSTOM_GUARD) +set(NDEF_SYMBOL INCLUDEGUARD_CUSTOM_EXPORT_H) +configure_file(main.cpp.in main_custom.cpp) +add_executable(test_includeguard_custom ${CMAKE_CURRENT_BINARY_DIR}/main_custom.cpp) diff --git a/Tests/RunCMake/GenerateExportHeader/includeguard/libincludeguard.cpp b/Tests/RunCMake/GenerateExportHeader/includeguard/libincludeguard.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/includeguard/libincludeguard.cpp diff --git a/Tests/RunCMake/GenerateExportHeader/includeguard/main.cpp.in b/Tests/RunCMake/GenerateExportHeader/includeguard/main.cpp.in new file mode 100644 index 0000000..51bd5d9 --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/includeguard/main.cpp.in @@ -0,0 +1,10 @@ +#include "@EXPORT_HEADER@" + +int main() +{ +#if defined(@DEF_SYMBOL@) && !defined(@NDEF_SYMBOL@) + return 0; +#else + return 1; +#endif +} diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h index e8af0a5..0b1dcba 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h index 598bd71..5e3ac9f 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h index cc20e09..dac4fda 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h index 4aaa848..b6e2a4a 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h index 053ad18..11f8042 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h index 4aaa848..b6e2a4a 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h index 808ff01..1481acd 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h index 4aaa848..b6e2a4a 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h index cc20e09..dac4fda 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h index 4aaa848..b6e2a4a 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h index 4e675af..3ba2d2e 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h index 4d5ed4e..3c7e093 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h index 84340e3..bf9163e 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h index 598bd71..5e3ac9f 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h @@ -39,4 +39,4 @@ # endif #endif -#endif +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/Make/IncludeRegexSubdir-check.cmake b/Tests/RunCMake/Make/IncludeRegexSubdir-check.cmake new file mode 100644 index 0000000..dbdf7be --- /dev/null +++ b/Tests/RunCMake/Make/IncludeRegexSubdir-check.cmake @@ -0,0 +1,4 @@ +include("${RunCMake_TEST_BINARY_DIR}/IncludeRegexSubdir/CMakeFiles/CMakeDirectoryInformation.cmake" OPTIONAL) +if(NOT CMAKE_C_INCLUDE_REGEX_SCAN STREQUAL "^custom_include_regex$") + set(RunCMake_TEST_FAILED "CMAKE_C_INCLUDE_REGEX_SCAN has unexpected value:\n \"${CMAKE_C_INCLUDE_REGEX_SCAN}\"") +endif() diff --git a/Tests/RunCMake/Make/IncludeRegexSubdir.cmake b/Tests/RunCMake/Make/IncludeRegexSubdir.cmake new file mode 100644 index 0000000..a7a93a0 --- /dev/null +++ b/Tests/RunCMake/Make/IncludeRegexSubdir.cmake @@ -0,0 +1,3 @@ +enable_language(C) +include_regular_expression("^custom_include_regex$") +add_subdirectory(IncludeRegexSubdir) diff --git a/Tests/RunCMake/Make/IncludeRegexSubdir/CMakeLists.txt b/Tests/RunCMake/Make/IncludeRegexSubdir/CMakeLists.txt new file mode 100644 index 0000000..e712aea --- /dev/null +++ b/Tests/RunCMake/Make/IncludeRegexSubdir/CMakeLists.txt @@ -0,0 +1 @@ +# We only need this directory to be processed. No targets needed. diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake index 869d11e..3b2b8f5 100644 --- a/Tests/RunCMake/Make/RunCMakeTest.cmake +++ b/Tests/RunCMake/Make/RunCMakeTest.cmake @@ -17,3 +17,4 @@ run_TargetMessages(VAR-ON -DCMAKE_TARGET_MESSAGES=ON) run_TargetMessages(VAR-OFF -DCMAKE_TARGET_MESSAGES=OFF) run_cmake(CustomCommandDepfile-ERROR) +run_cmake(IncludeRegexSubdir) diff --git a/Tests/RunCMake/SourceProperties/CMakeLists.txt b/Tests/RunCMake/SourceProperties/CMakeLists.txt new file mode 100644 index 0000000..a17c8cd --- /dev/null +++ b/Tests/RunCMake/SourceProperties/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 2.8.4) +project(${RunCMake_TEST} C) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/SourceProperties/RelativeIncludeDir-result.txt b/Tests/RunCMake/SourceProperties/RelativeIncludeDir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/SourceProperties/RelativeIncludeDir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/SourceProperties/RelativeIncludeDir-stderr.txt b/Tests/RunCMake/SourceProperties/RelativeIncludeDir-stderr.txt new file mode 100644 index 0000000..cf146c2 --- /dev/null +++ b/Tests/RunCMake/SourceProperties/RelativeIncludeDir-stderr.txt @@ -0,0 +1,4 @@ +CMake Error in CMakeLists.txt: + Found relative path while evaluating include directories of "empty.c": + + "relative" diff --git a/Tests/RunCMake/SourceProperties/RelativeIncludeDir.cmake b/Tests/RunCMake/SourceProperties/RelativeIncludeDir.cmake new file mode 100644 index 0000000..08dffa8 --- /dev/null +++ b/Tests/RunCMake/SourceProperties/RelativeIncludeDir.cmake @@ -0,0 +1,4 @@ + +set_property (SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "relative") + +add_library (somelib empty.c) diff --git a/Tests/RunCMake/SourceProperties/RunCMakeTest.cmake b/Tests/RunCMake/SourceProperties/RunCMakeTest.cmake new file mode 100644 index 0000000..0982153 --- /dev/null +++ b/Tests/RunCMake/SourceProperties/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(RelativeIncludeDir) diff --git a/Tests/RunCMake/SourceProperties/empty.c b/Tests/RunCMake/SourceProperties/empty.c new file mode 100644 index 0000000..a9ec102 --- /dev/null +++ b/Tests/RunCMake/SourceProperties/empty.c @@ -0,0 +1,5 @@ + +int empty() +{ + return 0; +} diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-result.txt b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-stderr.txt b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-stderr.txt new file mode 100644 index 0000000..f9b8ee7 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs-stderr.txt @@ -0,0 +1,8 @@ +^CMake Error in CMakeLists.txt: + Xcode does not support per-config per-source INCLUDE_DIRECTORIES: + + \$<\$<CONFIG:Debug>:MYDEBUG> + + specified for source: + + .*/Tests/RunCMake/XcodeProject/main.c$ diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs.cmake b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs.cmake new file mode 100644 index 0000000..4476c39 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceIncludeDirs.cmake @@ -0,0 +1,3 @@ +enable_language(C) +add_executable(main main.c) +set_property(SOURCE main.c PROPERTY INCLUDE_DIRECTORIES "$<$<CONFIG:Debug>:MYDEBUG>") diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-result.txt b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-stderr.txt b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-stderr.txt new file mode 100644 index 0000000..bfca020 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions-stderr.txt @@ -0,0 +1,8 @@ +^CMake Error in CMakeLists.txt: + Xcode does not support per-config per-source COMPILE_OPTIONS: + + \$<\$<CONFIG:Debug>:-DMYDEBUG> + + specified for source: + + .*/Tests/RunCMake/XcodeProject/main.c$ diff --git a/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions.cmake b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions.cmake new file mode 100644 index 0000000..f8e8030 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/PerConfigPerSourceOptions.cmake @@ -0,0 +1,3 @@ +enable_language(C) +add_executable(main main.c) +set_property(SOURCE main.c PROPERTY COMPILE_OPTIONS $<$<CONFIG:Debug>:-DMYDEBUG>) diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index 1313cb5..64a07f0 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake @@ -19,7 +19,9 @@ if (NOT XCODE_VERSION VERSION_LESS 6) endif() run_cmake(PerConfigPerSourceFlags) +run_cmake(PerConfigPerSourceOptions) run_cmake(PerConfigPerSourceDefinitions) +run_cmake(PerConfigPerSourceIncludeDirs) # Use a single build tree for a few tests without cleaning. diff --git a/Tests/Server/cmakelib.py b/Tests/Server/cmakelib.py index 6e8761a..edb5af6 100644 --- a/Tests/Server/cmakelib.py +++ b/Tests/Server/cmakelib.py @@ -288,7 +288,7 @@ def validateGlobalSettings(cmakeCommand, cmakeCommandPath, data): tmp = line.strip() if tmp.endswith(" [arch]"): tmp = tmp[0:len(tmp) - 7] - if (len(tmp) > 0) and (" - " not in tmp) and (tmp != 'KDevelop3'): + if (len(tmp) > 0) and (" - " not in tmp): cmakeGenerators.append(tmp) generators = [] diff --git a/Tests/SourceFileIncludeDirProperty/CMakeLists.txt b/Tests/SourceFileIncludeDirProperty/CMakeLists.txt new file mode 100644 index 0000000..786d5b6 --- /dev/null +++ b/Tests/SourceFileIncludeDirProperty/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.0) + +project(SourceFileIncludeDirProperty C) + +# +# Check that source level include directory take +# precedence over target one + +add_executable(SourceFileIncludeDirProperty main.c) + +set_property (TARGET SourceFileIncludeDirProperty + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/target") + +set_property (SOURCE main.c + PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/source") diff --git a/Tests/SourceFileIncludeDirProperty/main.c b/Tests/SourceFileIncludeDirProperty/main.c new file mode 100644 index 0000000..36144ca --- /dev/null +++ b/Tests/SourceFileIncludeDirProperty/main.c @@ -0,0 +1,7 @@ + +#include "header.h" + +int main() +{ + return 0; +} diff --git a/Tests/SourceFileIncludeDirProperty/source/header.h b/Tests/SourceFileIncludeDirProperty/source/header.h new file mode 100644 index 0000000..0c6f241 --- /dev/null +++ b/Tests/SourceFileIncludeDirProperty/source/header.h @@ -0,0 +1,2 @@ + +/* used header file */ diff --git a/Tests/SourceFileIncludeDirProperty/target/header.h b/Tests/SourceFileIncludeDirProperty/target/header.h new file mode 100644 index 0000000..71c1521 --- /dev/null +++ b/Tests/SourceFileIncludeDirProperty/target/header.h @@ -0,0 +1,2 @@ + +#error "wrong header file" diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index b244827..28803cb 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream <curl-library@cool.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-7_56_0" +readonly tag="curl-7_58_0" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/cmcurl/CMake/curl-config.cmake b/Utilities/cmcurl/CMake/curl-config.cmake new file mode 100644 index 0000000..119332c --- /dev/null +++ b/Utilities/cmcurl/CMake/curl-config.cmake @@ -0,0 +1,59 @@ + +get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if(NOT CURL_FIND_COMPONENTS) + set(CURL_FIND_COMPONENTS curl libcurl) + if(CURL_FIND_REQUIRED) + set(CURL_FIND_REQUIRED_curl TRUE) + set(CURL_FIND_REQUIRED_libcurl TRUE) + endif() +endif() + +set(_curl_missing_components) +foreach(_comp ${CURL_FIND_COMPONENTS}) + if(EXISTS "${_DIR}/${_comp}-target.cmake") + include("${_DIR}/${_comp}-target.cmake") + set(CURL_${_comp}_FOUND TRUE) + else() + set(CURL_${_comp}_FOUND FALSE) + if(CURL_FIND_REQUIRED_${_comp}) + set(CURL_FOUND FALSE) + list(APPEND _curl_missing_components ${_comp}) + endif() + endif() +endforeach() + +if(_curl_missing_components) + set(CURL_NOT_FOUND_MESSAGE "Following required components not found: " ${_curl_missing_components}) +else() + if(TARGET CURL::libcurl) + string(TOUPPER "${CMAKE_BUILD_TYPE}" _curl_current_config) + if(NOT _curl_current_config) + set(_curl_current_config "NOCONFIG") + endif() + get_target_property(_curl_configurations CURL::libcurl IMPORTED_CONFIGURATIONS) + list(FIND _curl_configurations "${_curl_current_config}" _i) + if(_i LESS 0) + set(_curl_config "RELEASE") + list(FIND _curl_configurations "${_curl_current_config}" _i) + if(_i LESS 0) + set(_curl_config "NOCONFIG") + list(FIND _curl_configurations "${_curl_current_config}" _i) + endif() + endif() + + if(_i LESS 0) + set(_curl_current_config "") # let CMake pick config at random + else() + set(_curl_current_config "_${_curl_current_config}") + endif() + + get_target_property(CURL_INCLUDE_DIRS CURL::libcurl INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(CURL_LIBRARIES CURL::libcurl "LOCATION${_curl_current_config}") + set(_curl_current_config) + set(_curl_configurations) + set(_i) + endif() +endif() + +unset(_curl_missing_components) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index a831f7f..148ca5d 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -123,7 +123,7 @@ endif() # To check: # (From Daniel Stenberg) The cmake build selected to run gcc with -fPIC on my box while the plain configure script did not. # (From Daniel Stenberg) The gcc command line use neither -g nor any -O options. As a developer, I also treasure our configure scripts's --enable-debug option that sets a long range of "picky" compiler options. -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) include(Macros) @@ -468,7 +468,6 @@ if(CMAKE_USE_OPENSSL) check_include_file("openssl/engine.h" HAVE_OPENSSL_ENGINE_H) check_include_file("openssl/err.h" HAVE_OPENSSL_ERR_H) check_include_file("openssl/pem.h" HAVE_OPENSSL_PEM_H) - check_include_file("openssl/pkcs12.h" HAVE_OPENSSL_PKCS12_H) check_include_file("openssl/rsa.h" HAVE_OPENSSL_RSA_H) check_include_file("openssl/ssl.h" HAVE_OPENSSL_SSL_H) check_include_file("openssl/x509.h" HAVE_OPENSSL_X509_H) @@ -868,7 +867,6 @@ check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) check_include_file_concat("io.h" HAVE_IO_H) check_include_file_concat("krb.h" HAVE_KRB_H) check_include_file_concat("libgen.h" HAVE_LIBGEN_H) -check_include_file_concat("limits.h" HAVE_LIMITS_H) check_include_file_concat("locale.h" HAVE_LOCALE_H) check_include_file_concat("net/if.h" HAVE_NET_IF_H) check_include_file_concat("netdb.h" HAVE_NETDB_H) @@ -997,10 +995,12 @@ check_symbol_exists(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE) check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME) check_symbol_exists(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT) check_symbol_exists(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE) +check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE) check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT) check_symbol_exists(fcntl "${CURL_INCLUDES}" HAVE_FCNTL) check_symbol_exists(ioctl "${CURL_INCLUDES}" HAVE_IOCTL) check_symbol_exists(setsockopt "${CURL_INCLUDES}" HAVE_SETSOCKOPT) +check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME) # symbol exists in win32, but function does not. if(WIN32) @@ -1253,6 +1253,12 @@ function(TRANSFORM_MAKEFILE_INC INPUT_FILE OUTPUT_FILE) endfunction() +if(WIN32 AND NOT CYGWIN) + set(CURL_INSTALL_CMAKE_DIR CMake) +else() + set(CURL_INSTALL_CMAKE_DIR lib/cmake/curl) +endif() + if(USE_MANUAL) add_subdirectory(docs) endif() @@ -1416,6 +1422,25 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" DESTINATION include FILES_MATCHING PATTERN "*.h") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/curl-config-version.cmake" + VERSION ${CURL_VERSION} + COMPATIBILITY SameMajorVersion +) + +configure_file(CMake/curl-config.cmake + "${PROJECT_BINARY_DIR}/curl-config.cmake" + COPYONLY +) + +install( + FILES ${PROJECT_BINARY_DIR}/curl-config.cmake + ${PROJECT_BINARY_DIR}/curl-config-version.cmake + DESTINATION ${CURL_INSTALL_CMAKE_DIR} +) + # Workaround for MSVS10 to avoid the Dialog Hell # FIXME: This could be removed with future version of CMake. if(MSVC_VERSION EQUAL 1600) diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING index 1e45a5e..560a49d 100644 --- a/Utilities/cmcurl/COPYING +++ b/Utilities/cmcurl/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2017, Daniel Stenberg, <daniel@haxx.se>, and many +Copyright (c) 1996 - 2018, Daniel Stenberg, <daniel@haxx.se>, and many contributors, see the THANKS file. All rights reserved. diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 5b4fd0f..a96124d 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -715,6 +715,7 @@ typedef enum { #define CURLSSH_AUTH_HOST (1<<2) /* host key files */ #define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ #define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ #define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY #define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ @@ -727,7 +728,9 @@ enum curl_khtype { CURLKHTYPE_UNKNOWN, CURLKHTYPE_RSA1, CURLKHTYPE_RSA, - CURLKHTYPE_DSS + CURLKHTYPE_DSS, + CURLKHTYPE_ECDSA, + CURLKHTYPE_ED25519 }; struct curl_khkey { @@ -935,7 +938,7 @@ typedef enum { CINIT(READDATA, OBJECTPOINT, 9), /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE - * bytes big. If this is not used, error messages go to stderr instead: */ + * bytes big. */ CINIT(ERRORBUFFER, OBJECTPOINT, 10), /* Function that will be called to store the output (instead of fwrite). The @@ -2514,7 +2517,7 @@ typedef enum { CURLCLOSEPOLICY_LAST /* last, never use this */ } curl_closepolicy; -#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */ #define CURL_GLOBAL_WIN32 (1<<1) #define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) #define CURL_GLOBAL_NOTHING 0 @@ -2592,6 +2595,7 @@ typedef enum { CURLVERSION_SECOND, CURLVERSION_THIRD, CURLVERSION_FOURTH, + CURLVERSION_FIFTH, CURLVERSION_LAST /* never actually use this */ } CURLversion; @@ -2600,7 +2604,7 @@ typedef enum { meant to be a built-in version number for what kind of struct the caller expects. If the struct ever changes, we redefine the NOW to another enum from above. */ -#define CURLVERSION_NOW CURLVERSION_FOURTH +#define CURLVERSION_NOW CURLVERSION_FIFTH typedef struct { CURLversion age; /* age of the returned struct */ @@ -2628,6 +2632,12 @@ typedef struct { const char *libssh_version; /* human readable string */ + /* These fields were added in CURLVERSION_FIFTH */ + + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *brotli_version; /* human readable string. */ + } curl_version_info_data; #define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ @@ -2658,6 +2668,7 @@ typedef struct { for cookie domain verification */ #define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ #define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ +#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ /* * NAME curl_version_info() diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 18dc356..a47c454 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -30,12 +30,12 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.56.0" +#define LIBCURL_VERSION "7.58.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 56 +#define LIBCURL_VERSION_MINOR 58 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -57,7 +57,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x073800 +#define LIBCURL_VERSION_NUM 0x073A00 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 39dae75..07bbd9c 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -348,7 +348,8 @@ defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ defined(__XTENSA__) || \ - (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)) + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" @@ -356,7 +357,8 @@ # define CURL_SUFFIX_CURL_OFF_TU ULL # elif defined(__LP64__) || \ defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ - (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index f3d9084..9f19daa 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -132,8 +132,25 @@ if(WIN32) endif() endif() +target_include_directories(${LIB_NAME} INTERFACE + $<INSTALL_INTERFACE:include>) + install(TARGETS ${LIB_NAME} + EXPORT libcurl-target ARCHIVE DESTINATION lib LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) + RUNTIME DESTINATION bin +) + +export(TARGETS ${LIB_NAME} + APPEND FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake + NAMESPACE CURL:: +) + +install(EXPORT libcurl-target + FILE libcurl-target.cmake + NAMESPACE CURL:: + DESTINATION ${CURL_INSTALL_CMAKE_DIR} +) + endif() diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 5c0b930..61e80cf 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. +# Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -46,7 +46,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ http_digest.c md4.c md5.c http_negotiate.c inet_pton.c strtoofft.c \ strerror.c amigaos.c hostasyn.c hostip4.c hostip6.c hostsyn.c \ inet_ntop.c parsedate.c select.c tftp.c splay.c strdup.c socks.c \ - ssh.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \ + ssh.c ssh-libssh.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \ curl_sspi.c slist.c nonblock.c curl_memrchr.c imap.c pop3.c smtp.c \ pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c \ openldap.c curl_gethostname.c gopher.c idn_win32.c \ @@ -54,7 +54,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \ curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \ x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \ - mime.c + mime.c sha256.c setopt.c curl_path.c LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ @@ -73,7 +73,8 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \ curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \ x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \ - curl_printf.h system_win32.h rand.h mime.h + curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \ + curl_path.h LIB_RCFILES = libcurl.rc diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 87e70b4..aa581a4 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -22,9 +22,15 @@ #include "curl_setup.h" -#ifdef HAVE_LIMITS_H +/*********************************************************************** + * Only for ares-enabled builds + * And only for functions that fulfill the asynch resolver backend API + * as defined in asyn.h, nothing else belongs in this file! + **********************************************************************/ + +#ifdef CURLRES_ARES + #include <limits.h> -#endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -48,14 +54,6 @@ #define in_addr_t unsigned long #endif -/*********************************************************************** - * Only for ares-enabled builds - * And only for functions that fulfill the asynch resolver backend API - * as defined in asyn.h, nothing else belongs in this file! - **********************************************************************/ - -#ifdef CURLRES_ARES - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -354,8 +352,8 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - long timeout; - struct curltime now = Curl_tvnow(); + timediff_t timeout; + struct curltime now = Curl_now(); struct Curl_dns_entry *temp_entry; if(entry) @@ -400,8 +398,8 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else { - struct curltime now2 = Curl_tvnow(); - time_t timediff = Curl_tvdiff(now2, now); /* spent time */ + struct curltime now2 = Curl_now(); + timediff_t timediff = Curl_timediff(now2, now); /* spent time */ if(timediff <= 0) timeout -= 1; /* always deduct at least 1 */ else if(timediff > timeout) diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index a867729..1ac3fc8 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -535,7 +535,8 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, } else { /* poll for name lookup done with exponential backoff up to 250ms */ - time_t elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + timediff_t elapsed = Curl_timediff(Curl_now(), + data->progress.t_startsingle); if(elapsed < 0) elapsed = 0; diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index c79d227..b8f5444 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -31,11 +31,36 @@ #include "multiif.h" #include "sendf.h" #include "conncache.h" +#include "share.h" +#include "sigpipe.h" +#include "connect.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +#ifdef CURLDEBUG +/* the debug versions of these macros make extra certain that the lock is + never doubly locked or unlocked */ +#define CONN_LOCK(x) if((x)->share) { \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \ + DEBUGASSERT(!(x)->state.conncache_lock); \ + (x)->state.conncache_lock = TRUE; \ + } + +#define CONN_UNLOCK(x) if((x)->share) { \ + DEBUGASSERT((x)->state.conncache_lock); \ + (x)->state.conncache_lock = FALSE; \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ + } +#else +#define CONN_LOCK(x) if((x)->share) \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) +#define CONN_UNLOCK(x) if((x)->share) \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) +#endif + static void conn_llist_dtor(void *user, void *element) { struct connectdata *data = element; @@ -109,8 +134,23 @@ static void free_bundle_hash_entry(void *freethis) int Curl_conncache_init(struct conncache *connc, int size) { - return Curl_hash_init(&connc->hash, size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); + int rc; + + /* allocate a new easy handle to use when closing cached connections */ + connc->closure_handle = curl_easy_init(); + if(!connc->closure_handle) + return 1; /* bad */ + + rc = Curl_hash_init(&connc->hash, size, Curl_hash_str, + Curl_str_key_compare, free_bundle_hash_entry); + if(rc) { + Curl_close(connc->closure_handle); + connc->closure_handle = NULL; + } + else + connc->closure_handle->state.conn_cache = connc; + + return rc; } void Curl_conncache_destroy(struct conncache *connc) @@ -140,12 +180,44 @@ static void hashkey(struct connectdata *conn, char *buf, snprintf(buf, len, "%ld%s", conn->port, hostname); } +void Curl_conncache_unlock(struct connectdata *conn) +{ + CONN_UNLOCK(conn->data); +} + +/* Returns number of connections currently held in the connection cache. + Locks/unlocks the cache itself! +*/ +size_t Curl_conncache_size(struct Curl_easy *data) +{ + size_t num; + CONN_LOCK(data); + num = data->state.conn_cache->num_conn; + CONN_UNLOCK(data); + return num; +} + +/* Returns number of connections currently held in the connections's bundle + Locks/unlocks the cache itself! +*/ +size_t Curl_conncache_bundle_size(struct connectdata *conn) +{ + size_t num; + CONN_LOCK(conn->data); + num = conn->bundle->num_connections; + CONN_UNLOCK(conn->data); + return num; +} + /* Look up the bundle with all the connections to the same host this - connectdata struct is setup to use. */ + connectdata struct is setup to use. + + **NOTE**: When it returns, it holds the connection cache lock! */ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, struct conncache *connc) { struct connectbundle *bundle = NULL; + CONN_LOCK(conn->data); if(connc) { char key[128]; hashkey(conn, key, sizeof(key)); @@ -196,21 +268,24 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectbundle *new_bundle = NULL; struct Curl_easy *data = conn->data; + /* *find_bundle() locks the connection cache */ bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); if(!bundle) { int rc; char key[128]; result = bundle_create(data, &new_bundle); - if(result) - return result; + if(result) { + goto unlock; + } hashkey(conn, key, sizeof(key)); rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle); if(!rc) { bundle_destroy(new_bundle); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto unlock; } bundle = new_bundle; } @@ -219,49 +294,64 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, if(result) { if(new_bundle) conncache_remove_bundle(data->state.conn_cache, new_bundle); - return result; + goto unlock; } conn->connection_id = connc->next_connection_id++; - connc->num_connections++; + connc->num_conn++; DEBUGF(infof(conn->data, "Added connection %ld. " "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n", - conn->connection_id, (curl_off_t) connc->num_connections)); + conn->connection_id, (curl_off_t) connc->num_conn)); - return CURLE_OK; + unlock: + CONN_UNLOCK(data); + + return result; } -void Curl_conncache_remove_conn(struct conncache *connc, - struct connectdata *conn) +void Curl_conncache_remove_conn(struct connectdata *conn, bool lock) { + struct Curl_easy *data = conn->data; struct connectbundle *bundle = conn->bundle; + struct conncache *connc = data->state.conn_cache; /* The bundle pointer can be NULL, since this function can be called due to a failed connection attempt, before being added to a bundle */ if(bundle) { + if(lock) { + CONN_LOCK(conn->data); + } bundle_remove_conn(bundle, conn); - if(bundle->num_connections == 0) { + if(bundle->num_connections == 0) conncache_remove_bundle(connc, bundle); - } - + conn->bundle = NULL; /* removed from it */ if(connc) { - connc->num_connections--; - + connc->num_conn--; DEBUGF(infof(conn->data, "The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n", - (curl_off_t) connc->num_connections)); + (curl_off_t) connc->num_conn)); + } + if(lock) { + CONN_UNLOCK(conn->data); } } } -/* This function iterates the entire connection cache and calls the - function func() with the connection pointer as the first argument - and the supplied 'param' argument as the other, +/* This function iterates the entire connection cache and calls the function + func() with the connection pointer as the first argument and the supplied + 'param' argument as the other. + + The conncache lock is still held when the callback is called. It needs it, + so that it can safely continue traversing the lists once the callback + returns. + + Returns 1 if the loop was aborted due to the callback's return code. Return 0 from func() to continue the loop, return 1 to abort it. */ -void Curl_conncache_foreach(struct conncache *connc, +bool Curl_conncache_foreach(struct Curl_easy *data, + struct conncache *connc, void *param, int (*func)(struct connectdata *conn, void *param)) { @@ -270,8 +360,9 @@ void Curl_conncache_foreach(struct conncache *connc, struct curl_hash_element *he; if(!connc) - return; + return FALSE; + CONN_LOCK(data); Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); @@ -288,14 +379,22 @@ void Curl_conncache_foreach(struct conncache *connc, struct connectdata *conn = curr->ptr; curr = curr->next; - if(1 == func(conn, param)) - return; + if(1 == func(conn, param)) { + CONN_UNLOCK(data); + return TRUE; + } } } + CONN_UNLOCK(data); + return FALSE; } /* Return the first connection found in the cache. Used when closing all - connections */ + connections. + + NOTE: no locking is done here as this is presumably only done when cleaning + up a cache! +*/ struct connectdata * Curl_conncache_find_first_connection(struct conncache *connc) { @@ -321,6 +420,188 @@ Curl_conncache_find_first_connection(struct conncache *connc) return NULL; } +/* + * Give ownership of a connection back to the connection cache. Might + * disconnect the oldest existing in there to make space. + * + * Return TRUE if stored, FALSE if closed. + */ +bool Curl_conncache_return_conn(struct connectdata *conn) +{ + struct Curl_easy *data = conn->data; + + /* data->multi->maxconnects can be negative, deal with it. */ + size_t maxconnects = + (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: + data->multi->maxconnects; + struct connectdata *conn_candidate = NULL; + + if(maxconnects > 0 && + Curl_conncache_size(data) > maxconnects) { + infof(data, "Connection cache is full, closing the oldest one.\n"); + + conn_candidate = Curl_conncache_extract_oldest(data); + + if(conn_candidate) { + /* Set the connection's owner correctly */ + conn_candidate->data = data; + + /* the winner gets the honour of being disconnected */ + (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); + } + } + CONN_LOCK(data); + conn->inuse = FALSE; /* Mark the connection unused */ + CONN_UNLOCK(data); + + return (conn_candidate == conn) ? FALSE : TRUE; + +} + +/* + * This function finds the connection in the connection bundle that has been + * unused for the longest time. + * + * Does not lock the connection cache! + * + * Returns the pointer to the oldest idle connection, or NULL if none was + * found. + */ +struct connectdata * +Curl_conncache_extract_bundle(struct Curl_easy *data, + struct connectbundle *bundle) +{ + struct curl_llist_element *curr; + timediff_t highscore = -1; + timediff_t score; + struct curltime now; + struct connectdata *conn_candidate = NULL; + struct connectdata *conn; + + (void)data; + + now = Curl_now(); + + curr = bundle->conn_list.head; + while(curr) { + conn = curr->ptr; + + if(!conn->inuse) { + /* Set higher score for the age passed since the connection was used */ + score = Curl_timediff(now, conn->now); + + if(score > highscore) { + highscore = score; + conn_candidate = conn; + } + } + curr = curr->next; + } + if(conn_candidate) { + /* remove it to prevent another thread from nicking it */ + bundle_remove_conn(bundle, conn_candidate); + data->state.conn_cache->num_conn--; + DEBUGF(infof(data, "The cache now contains %" + CURL_FORMAT_CURL_OFF_TU " members\n", + (curl_off_t) data->state.conn_cache->num_conn)); + } + + return conn_candidate; +} + +/* + * This function finds the connection in the connection cache that has been + * unused for the longest time and extracts that from the bundle. + * + * Returns the pointer to the connection, or NULL if none was found. + */ +struct connectdata * +Curl_conncache_extract_oldest(struct Curl_easy *data) +{ + struct conncache *connc = data->state.conn_cache; + struct curl_hash_iterator iter; + struct curl_llist_element *curr; + struct curl_hash_element *he; + timediff_t highscore =- 1; + timediff_t score; + struct curltime now; + struct connectdata *conn_candidate = NULL; + struct connectbundle *bundle; + struct connectbundle *bundle_candidate = NULL; + + now = Curl_now(); + + CONN_LOCK(data); + Curl_hash_start_iterate(&connc->hash, &iter); + + he = Curl_hash_next_element(&iter); + while(he) { + struct connectdata *conn; + + bundle = he->ptr; + + curr = bundle->conn_list.head; + while(curr) { + conn = curr->ptr; + + if(!conn->inuse) { + /* Set higher score for the age passed since the connection was used */ + score = Curl_timediff(now, conn->now); + + if(score > highscore) { + highscore = score; + conn_candidate = conn; + bundle_candidate = bundle; + } + } + curr = curr->next; + } + + he = Curl_hash_next_element(&iter); + } + if(conn_candidate) { + /* remove it to prevent another thread from nicking it */ + bundle_remove_conn(bundle_candidate, conn_candidate); + connc->num_conn--; + DEBUGF(infof(data, "The cache now contains %" + CURL_FORMAT_CURL_OFF_TU " members\n", + (curl_off_t) connc->num_conn)); + } + CONN_UNLOCK(data); + + return conn_candidate; +} + +void Curl_conncache_close_all_connections(struct conncache *connc) +{ + struct connectdata *conn; + + conn = Curl_conncache_find_first_connection(connc); + while(conn) { + SIGPIPE_VARIABLE(pipe_st); + conn->data = connc->closure_handle; + + sigpipe_ignore(conn->data, &pipe_st); + conn->data->easy_conn = NULL; /* clear the easy handle's connection + pointer */ + /* This will remove the connection from the cache */ + connclose(conn, "kill all"); + (void)Curl_disconnect(conn, FALSE); + sigpipe_restore(&pipe_st); + + conn = Curl_conncache_find_first_connection(connc); + } + + if(connc->closure_handle) { + SIGPIPE_VARIABLE(pipe_st); + sigpipe_ignore(connc->closure_handle, &pipe_st); + + Curl_hostcache_clean(connc->closure_handle, + connc->closure_handle->dns.hostcache); + Curl_close(connc->closure_handle); + sigpipe_restore(&pipe_st); + } +} #if 0 /* Useful for debugging the connection cache */ diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index 14be4e8..d8ad80f 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h @@ -23,11 +23,19 @@ * ***************************************************************************/ +/* + * All accesses to struct fields and changing of data in the connection cache + * and connectbundles must be done with the conncache LOCKED. The cache might + * be shared. + */ + struct conncache { struct curl_hash hash; - size_t num_connections; + size_t num_conn; long next_connection_id; struct curltime last_cleanup; + /* handle used for closing cached connections */ + struct Curl_easy *closure_handle; }; #define BUNDLE_NO_MULTIUSE -1 @@ -41,21 +49,25 @@ struct connectbundle { struct curl_llist conn_list; /* The connectdata members of the bundle */ }; +/* returns 1 on error, 0 is fine */ int Curl_conncache_init(struct conncache *, int size); - void Curl_conncache_destroy(struct conncache *connc); /* return the correct bundle, to a host or a proxy */ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, struct conncache *connc); +void Curl_conncache_unlock(struct connectdata *conn); +/* returns number of connections currently held in the connection cache */ +size_t Curl_conncache_size(struct Curl_easy *data); +size_t Curl_conncache_bundle_size(struct connectdata *conn); +bool Curl_conncache_return_conn(struct connectdata *conn); CURLcode Curl_conncache_add_conn(struct conncache *connc, struct connectdata *conn); - -void Curl_conncache_remove_conn(struct conncache *connc, - struct connectdata *conn); - -void Curl_conncache_foreach(struct conncache *connc, +void Curl_conncache_remove_conn(struct connectdata *conn, + bool lock); +bool Curl_conncache_foreach(struct Curl_easy *data, + struct conncache *connc, void *param, int (*func)(struct connectdata *conn, void *param)); @@ -63,6 +75,12 @@ void Curl_conncache_foreach(struct conncache *connc, struct connectdata * Curl_conncache_find_first_connection(struct conncache *connc); +struct connectdata * +Curl_conncache_extract_bundle(struct Curl_easy *data, + struct connectbundle *bundle); +struct connectdata * +Curl_conncache_extract_oldest(struct Curl_easy *data); +void Curl_conncache_close_all_connections(struct conncache *connc); void Curl_conncache_print(struct conncache *connc); #endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index b7d10af..3edb71e 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -28,8 +28,10 @@ #ifdef HAVE_SYS_UN_H #include <sys/un.h> /* for sockaddr_un */ #endif -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> /* for TCP_NODELAY */ +#ifdef HAVE_LINUX_TCP_H +#include <linux/tcp.h> +#elif defined(HAVE_NETINET_TCP_H) +#include <netinet/tcp.h> #endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> @@ -179,12 +181,12 @@ singleipconnect(struct connectdata *conn, * * @unittest: 1303 */ -time_t Curl_timeleft(struct Curl_easy *data, - struct curltime *nowp, - bool duringconnect) +timediff_t Curl_timeleft(struct Curl_easy *data, + struct curltime *nowp, + bool duringconnect) { int timeout_set = 0; - time_t timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0; + timediff_t timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0; struct curltime now; /* if a timeout is set, use the most restrictive one */ @@ -218,17 +220,17 @@ time_t Curl_timeleft(struct Curl_easy *data, } if(!nowp) { - now = Curl_tvnow(); + now = Curl_now(); nowp = &now; } /* subtract elapsed time */ if(duringconnect) /* since this most recent connect started */ - timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle); + timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle); else /* since the entire operation started */ - timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startop); + timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop); if(!timeout_ms) /* avoid returning 0 as that means no timeout! */ return -1; @@ -285,6 +287,34 @@ static CURLcode bindlocal(struct connectdata *conn, /* interface */ if(!is_host) { +#ifdef SO_BINDTODEVICE + /* I am not sure any other OSs than Linux that provide this feature, + * and at the least I cannot test. --Ben + * + * This feature allows one to tightly bind the local socket to a + * particular interface. This will force even requests to other + * local interfaces to go out the external interface. + * + * + * Only bind to the interface when specified as interface, not just + * as a hostname or ip address. + * + * interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try + * to use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + dev, (curl_socklen_t)strlen(dev) + 1) == 0) { + /* This is typically "errno 1, error: Operation not permitted" if + * you're not running as root or another suitable privileged + * user. + * If it succeeds it means the parameter was a valid interface and + * not an IP address. Return immediately. + */ + return CURLE_OK; + } +#endif + switch(Curl_if2ip(af, scope, conn->scope_id, dev, myhost, sizeof(myhost))) { case IF2IP_NOT_FOUND: @@ -305,30 +335,6 @@ static CURLcode bindlocal(struct connectdata *conn, infof(data, "Local Interface %s is ip %s using address family %i\n", dev, myhost, af); done = 1; - -#ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev) + 1) != 0) { - error = SOCKERRNO; - infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;" - " will do regular bind\n", - dev, error, Curl_strerror(conn, error)); - /* This is typically "errno 1, error: Operation not permitted" if - you're not running as root or another suitable privileged - user */ - } -#endif break; } } @@ -408,6 +414,10 @@ static CURLcode bindlocal(struct connectdata *conn, } if(done < 1) { + /* errorbuf is set false so failf will overwrite any message already in + the error buffer, so the user receives this error message instead of a + generic resolve error. */ + data->state.errorbuf = FALSE; failf(data, "Couldn't bind to '%s'", dev); return CURLE_INTERFACE_FAILED; } @@ -721,7 +731,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, { struct Curl_easy *data = conn->data; CURLcode result = CURLE_OK; - time_t allow; + timediff_t allow; int error = 0; struct curltime now; int rc; @@ -737,7 +747,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, return CURLE_OK; } - now = Curl_tvnow(); + now = Curl_now(); /* figure out how long time we have left to connect */ allow = Curl_timeleft(data, &now, TRUE); @@ -765,7 +775,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, if(rc == 0) { /* no connection yet */ error = 0; - if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) { + if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) { infof(data, "After %ldms connect time, move on!\n", conn->timeoutms_per_addr); error = ETIMEDOUT; @@ -773,7 +783,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, /* should we try another protocol family? */ if(i == 0 && conn->tempaddr[1] == NULL && - curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) { + Curl_timediff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) { trynextip(conn, sockindex, 1); } } @@ -785,6 +795,9 @@ CURLcode Curl_is_connected(struct connectdata *conn, conn->sock[sockindex] = conn->tempsock[i]; conn->ip_addr = conn->tempaddr[i]; conn->tempsock[i] = CURL_SOCKET_BAD; +#ifdef ENABLE_IPV6 + conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE; +#endif /* close the other socket, if open */ if(conn->tempsock[other] != CURL_SOCKET_BAD) { @@ -978,6 +991,9 @@ static CURLcode singleipconnect(struct connectdata *conn, char ipaddress[MAX_IPADR_LEN]; long port; bool is_tcp; +#ifdef TCP_FASTOPEN_CONNECT + int optval = 1; +#endif *sockp = CURL_SOCKET_BAD; @@ -1051,17 +1067,19 @@ static CURLcode singleipconnect(struct connectdata *conn, /* set socket non-blocking */ (void)curlx_nonblock(sockfd, TRUE); - conn->connecttime = Curl_tvnow(); + conn->connecttime = Curl_now(); if(conn->num_addr > 1) Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { if(conn->bits.tcp_fastopen) { -#if defined(CONNECT_DATA_IDEMPOTENT) /* OS X */ -#ifdef HAVE_BUILTIN_AVAILABLE +#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ +# if defined(HAVE_BUILTIN_AVAILABLE) + /* while connectx function is available since macOS 10.11 / iOS 9, + it did not have the interface declared correctly until + Xcode 9 / macOS SDK 10.13 */ if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { -#endif /* HAVE_BUILTIN_AVAILABLE */ sa_endpoints_t endpoints; endpoints.sae_srcif = 0; endpoints.sae_srcaddr = NULL; @@ -1072,13 +1090,22 @@ static CURLcode singleipconnect(struct connectdata *conn, rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, NULL, 0, NULL, NULL); -#ifdef HAVE_BUILTIN_AVAILABLE } else { rc = connect(sockfd, &addr.sa_addr, addr.addrlen); } -#endif /* HAVE_BUILTIN_AVAILABLE */ -#elif defined(MSG_FASTOPEN) /* Linux */ +# else + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); +# endif /* HAVE_BUILTIN_AVAILABLE */ +#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ + if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + (void *)&optval, sizeof(optval)) < 0) + infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd); + else + infof(data, "TCP_FASTOPEN_CONNECT set\n"); + + rc = connect(sockfd, &addr.sa_addr, addr.addrlen); +#elif defined(MSG_FASTOPEN) /* old Linux */ if(conn->given->flags & PROTOPT_SSL) rc = connect(sockfd, &addr.sa_addr, addr.addrlen); else @@ -1097,10 +1124,6 @@ static CURLcode singleipconnect(struct connectdata *conn, return CURLE_OK; } -#ifdef ENABLE_IPV6 - conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE; -#endif - if(-1 == rc) { switch(error) { case EINPROGRESS: @@ -1145,10 +1168,10 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ const struct Curl_dns_entry *remotehost) { struct Curl_easy *data = conn->data; - struct curltime before = Curl_tvnow(); + struct curltime before = Curl_now(); CURLcode result = CURLE_COULDNT_CONNECT; - time_t timeout_ms = Curl_timeleft(data, &before, TRUE); + timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE); if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ @@ -1225,7 +1248,7 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, find.tofind = data->state.lastconnect; find.found = FALSE; - Curl_conncache_foreach(data->multi_easy? + Curl_conncache_foreach(data, data->multi_easy? &data->multi_easy->conn_cache: &data->multi->conn_cache, &find, conn_is_conn); diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 3f05c39..3974486 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h @@ -25,6 +25,7 @@ #include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ #include "sockaddr.h" +#include "timeval.h" CURLcode Curl_is_connected(struct connectdata *conn, int sockindex, @@ -35,9 +36,9 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* generic function that returns how much time there's left to run, according to the timeouts set */ -time_t Curl_timeleft(struct Curl_easy *data, - struct curltime *nowp, - bool duringconnect); +timediff_t Curl_timeleft(struct Curl_easy *data, + struct curltime *nowp, + bool duringconnect); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ #define HAPPY_EYEBALLS_TIMEOUT 200 /* milliseconds to wait between diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 1102260..46bef0c 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -22,22 +22,43 @@ #include "curl_setup.h" -#ifdef HAVE_LIBZ - #include "urldata.h" #include <curl/curl.h> +#include <stddef.h> + +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#ifdef __SYMBIAN32__ +/* zlib pollutes the namespace with this definition */ +#undef WIN32 +#endif +#endif + +#ifdef HAVE_BROTLI +#include <brotli/decode.h> +#endif + #include "sendf.h" +#include "http.h" #include "content_encoding.h" #include "strdup.h" +#include "strcase.h" #include "curl_memory.h" #include "memdebug.h" +#define CONTENT_ENCODING_DEFAULT "identity" + +#ifndef CURL_DISABLE_HTTP + +#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ + + +#ifdef HAVE_LIBZ + /* Comment this out if zlib is always going to be at least ver. 1.2.0.4 (doing so will reduce code size slightly). */ #define OLD_ZLIB_SUPPORT 1 -#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ - #define GZIP_MAGIC_0 0x1f #define GZIP_MAGIC_1 0x8b @@ -49,6 +70,24 @@ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ +typedef enum { + ZLIB_UNINIT, /* uninitialized */ + ZLIB_INIT, /* initialized */ + ZLIB_INFLATING, /* Inflating started. */ + ZLIB_GZIP_HEADER, /* reading gzip header */ + ZLIB_GZIP_TRAILER, /* reading gzip trailer */ + ZLIB_GZIP_INFLATING, /* inflating gzip stream */ + ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ +} zlibInitState; + +/* Writer parameters. */ +typedef struct { + zlibInitState zlib_init; /* zlib init state */ + uInt trailerlen; /* Remaining trailer byte count. */ + z_stream z; /* State structure for zlib. */ +} zlib_params; + + static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { @@ -79,114 +118,223 @@ process_zlib_error(struct connectdata *conn, z_stream *z) } static CURLcode -exit_zlib(z_stream *z, zlibInitState *zlib_init, CURLcode result) +exit_zlib(struct connectdata *conn, + z_stream *z, zlibInitState *zlib_init, CURLcode result) { - inflateEnd(z); - *zlib_init = ZLIB_UNINIT; + if(*zlib_init == ZLIB_GZIP_HEADER) + Curl_safefree(z->next_in); + + if(*zlib_init != ZLIB_UNINIT) { + if(inflateEnd(z) != Z_OK && result == CURLE_OK) + result = process_zlib_error(conn, z); + *zlib_init = ZLIB_UNINIT; + } + return result; } -static CURLcode -inflate_stream(struct connectdata *conn, - struct SingleRequest *k) +static CURLcode process_trailer(struct connectdata *conn, zlib_params *zp) { - int allow_restart = 1; - z_stream *z = &k->z; /* zlib state structure */ + z_stream *z = &zp->z; + CURLcode result = CURLE_OK; + uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen; + + /* Consume expected trailer bytes. Terminate stream if exhausted. + Issue an error if unexpected bytes follow. */ + + zp->trailerlen -= len; + z->avail_in -= len; + z->next_in += len; + if(z->avail_in) + result = CURLE_WRITE_ERROR; + if(result || !zp->trailerlen) + result = exit_zlib(conn, z, &zp->zlib_init, result); + else { + /* Only occurs for gzip with zlib < 1.2.0.4. */ + zp->zlib_init = ZLIB_GZIP_TRAILER; + } + return result; +} + +static CURLcode inflate_stream(struct connectdata *conn, + contenc_writer *writer, zlibInitState started) +{ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ uInt nread = z->avail_in; Bytef *orig_in = z->next_in; int status; /* zlib status */ + bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ char *decomp; /* Put the decompressed data here. */ + /* Check state. */ + if(zp->zlib_init != ZLIB_INIT && + zp->zlib_init != ZLIB_INFLATING && + zp->zlib_init != ZLIB_INIT_GZIP && + zp->zlib_init != ZLIB_GZIP_INFLATING) + return exit_zlib(conn, z, &zp->zlib_init, CURLE_WRITE_ERROR); + /* Dynamically allocate a buffer for decompression because it's uncommonly large to hold on the stack */ decomp = malloc(DSIZ); - if(decomp == NULL) { - return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY); - } + if(decomp == NULL) + return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); /* because the buffer size is fixed, iteratively decompress and transfer to - the client via client_write. */ - for(;;) { + the client via downstream_write function. */ + while(!done) { + done = TRUE; + /* (re)set buffer for decompressed output for every iteration */ - z->next_out = (Bytef *)decomp; + z->next_out = (Bytef *) decomp; z->avail_out = DSIZ; - status = inflate(z, Z_SYNC_FLUSH); - if(status == Z_OK || status == Z_STREAM_END) { - allow_restart = 0; - if((DSIZ - z->avail_out) && (!k->ignorebody)) { - result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp, - DSIZ - z->avail_out); - /* if !CURLE_OK, clean up, return */ + status = inflate(z, Z_BLOCK); + + /* Flush output data if some. */ + if(z->avail_out != DSIZ) { + if(status == Z_OK || status == Z_STREAM_END) { + zp->zlib_init = started; /* Data started. */ + result = Curl_unencode_write(conn, writer->downstream, decomp, + DSIZ - z->avail_out); if(result) { - free(decomp); - return exit_zlib(z, &k->zlib_init, result); + exit_zlib(conn, z, &zp->zlib_init, result); + break; } } - - /* Done? clean up, return */ - if(status == Z_STREAM_END) { - free(decomp); - if(inflateEnd(z) == Z_OK) - return exit_zlib(z, &k->zlib_init, result); - return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); - } - - /* Done with these bytes, exit */ - - /* status is always Z_OK at this point! */ - if(z->avail_in == 0) { - free(decomp); - return result; - } } - else if(allow_restart && status == Z_DATA_ERROR) { + + /* Dispatch by inflate() status. */ + switch(status) { + case Z_OK: + /* Always loop: there may be unflushed latched data in zlib state. */ + done = FALSE; + break; + case Z_BUF_ERROR: + /* No more data to flush: just exit loop. */ + break; + case Z_STREAM_END: + result = process_trailer(conn, zp); + break; + case Z_DATA_ERROR: /* some servers seem to not generate zlib headers, so this is an attempt to fix and continue anyway */ - - (void) inflateEnd(z); /* don't care about the return code */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - free(decomp); - return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); + if(zp->zlib_init == ZLIB_INIT) { + /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ + (void) inflateEnd(z); /* don't care about the return code */ + if(inflateInit2(z, -MAX_WBITS) == Z_OK) { + z->next_in = orig_in; + z->avail_in = nread; + zp->zlib_init = ZLIB_INFLATING; + done = FALSE; + break; + } + zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } - z->next_in = orig_in; - z->avail_in = nread; - allow_restart = 0; - continue; - } - else { /* Error; exit loop, handle below */ - free(decomp); - return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); + /* FALLTHROUGH */ + default: + result = exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z)); + break; } } - /* Will never get here */ + free(decomp); + + /* We're about to leave this call so the `nread' data bytes won't be seen + again. If we are in a state that would wrongly allow restart in raw mode + at the next call, assume output has already started. */ + if(nread && zp->zlib_init == ZLIB_INIT) + zp->zlib_init = started; /* Cannot restart anymore. */ + + return result; } -CURLcode -Curl_unencode_deflate_write(struct connectdata *conn, - struct SingleRequest *k, - ssize_t nread) + +/* Deflate handler. */ +static CURLcode deflate_init_writer(struct connectdata *conn, + contenc_writer *writer) { - z_stream *z = &k->z; /* zlib state structure */ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ - /* Initialize zlib? */ - if(k->zlib_init == ZLIB_UNINIT) { - memset(z, 0, sizeof(z_stream)); - z->zalloc = (alloc_func)zalloc_cb; - z->zfree = (free_func)zfree_cb; + if(!writer->downstream) + return CURLE_WRITE_ERROR; - if(inflateInit(z) != Z_OK) - return process_zlib_error(conn, z); - k->zlib_init = ZLIB_INIT; - } + /* Initialize zlib */ + z->zalloc = (alloc_func) zalloc_cb; + z->zfree = (free_func) zfree_cb; + + if(inflateInit(z) != Z_OK) + return process_zlib_error(conn, z); + zp->zlib_init = ZLIB_INIT; + return CURLE_OK; +} + +static CURLcode deflate_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) +{ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ /* Set the compressed input when this function is called */ - z->next_in = (Bytef *)k->str; - z->avail_in = (uInt)nread; + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; /* Now uncompress the data */ - return inflate_stream(conn, k); + return inflate_stream(conn, writer, ZLIB_INFLATING); +} + +static void deflate_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ + + exit_zlib(conn, z, &zp->zlib_init, CURLE_OK); +} + +static const content_encoding deflate_encoding = { + "deflate", + NULL, + deflate_init_writer, + deflate_unencode_write, + deflate_close_writer, + sizeof(zlib_params) +}; + + +/* Gzip handler. */ +static CURLcode gzip_init_writer(struct connectdata *conn, + contenc_writer *writer) +{ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ + + if(!writer->downstream) + return CURLE_WRITE_ERROR; + + /* Initialize zlib */ + z->zalloc = (alloc_func) zalloc_cb; + z->zfree = (free_func) zfree_cb; + + if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { + /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ + if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { + return process_zlib_error(conn, z); + } + zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ + } + else { + /* we must parse the gzip header and trailer ourselves */ + if(inflateInit2(z, -MAX_WBITS) != Z_OK) { + return process_zlib_error(conn, z); + } + zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ + zp->zlib_init = ZLIB_INIT; /* Initial call state */ + } + + return CURLE_OK; } #ifdef OLD_ZLIB_SUPPORT @@ -273,47 +421,25 @@ static enum { } #endif -CURLcode -Curl_unencode_gzip_write(struct connectdata *conn, - struct SingleRequest *k, - ssize_t nread) +static CURLcode gzip_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) { - z_stream *z = &k->z; /* zlib state structure */ - - /* Initialize zlib? */ - if(k->zlib_init == ZLIB_UNINIT) { - memset(z, 0, sizeof(z_stream)); - z->zalloc = (alloc_func)zalloc_cb; - z->zfree = (free_func)zfree_cb; - - if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { - /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ - if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { - return process_zlib_error(conn, z); - } - k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ - } - else { - /* we must parse the gzip header ourselves */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - return process_zlib_error(conn, z); - } - k->zlib_init = ZLIB_INIT; /* Initial call state */ - } - } + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ - if(k->zlib_init == ZLIB_INIT_GZIP) { + if(zp->zlib_init == ZLIB_INIT_GZIP) { /* Let zlib handle the gzip decompression entirely */ - z->next_in = (Bytef *)k->str; - z->avail_in = (uInt)nread; + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; /* Now uncompress the data */ - return inflate_stream(conn, k); + return inflate_stream(conn, writer, ZLIB_INIT_GZIP); } #ifndef OLD_ZLIB_SUPPORT /* Support for old zlib versions is compiled away and we are running with an old version, so return an error. */ - return exit_zlib(z, &k->zlib_init, CURLE_WRITE_ERROR); + return exit_zlib(conn, z, &zp->zlib_init, CURLE_WRITE_ERROR); #else /* This next mess is to get around the potential case where there isn't @@ -326,18 +452,18 @@ Curl_unencode_gzip_write(struct connectdata *conn, * can handle the gzip header themselves. */ - switch(k->zlib_init) { + switch(zp->zlib_init) { /* Skip over gzip header? */ case ZLIB_INIT: { /* Initial call state */ ssize_t hlen; - switch(check_gzip_header((unsigned char *)k->str, nread, &hlen)) { + switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { case GZIP_OK: - z->next_in = (Bytef *)k->str + hlen; - z->avail_in = (uInt)(nread - hlen); - k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ + z->next_in = (Bytef *) buf + hlen; + z->avail_in = (uInt) (nbytes - hlen); + zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: @@ -348,19 +474,19 @@ Curl_unencode_gzip_write(struct connectdata *conn, * the first place, and it's even more unlikely for a transfer to fail * immediately afterwards, it should seldom be a problem. */ - z->avail_in = (uInt)nread; + z->avail_in = (uInt) nbytes; z->next_in = malloc(z->avail_in); if(z->next_in == NULL) { - return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY); + return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } - memcpy(z->next_in, k->str, z->avail_in); - k->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ + memcpy(z->next_in, buf, z->avail_in); + zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ /* We don't have any data to inflate yet */ return CURLE_OK; case GZIP_BAD: default: - return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); + return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z)); } } @@ -370,22 +496,22 @@ Curl_unencode_gzip_write(struct connectdata *conn, { /* Need more gzip header data state */ ssize_t hlen; - z->avail_in += (uInt)nread; + z->avail_in += (uInt) nbytes; z->next_in = Curl_saferealloc(z->next_in, z->avail_in); if(z->next_in == NULL) { - return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY); + return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } /* Append the new block of data to the previous one */ - memcpy(z->next_in + z->avail_in - nread, k->str, nread); + memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { case GZIP_OK: /* This is the zlib stream data */ free(z->next_in); /* Don't point into the malloced block since we just freed it */ - z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in; - z->avail_in = (uInt)(z->avail_in - hlen); - k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ + z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; + z->avail_in = (uInt) (z->avail_in - hlen); + zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: @@ -394,18 +520,22 @@ Curl_unencode_gzip_write(struct connectdata *conn, case GZIP_BAD: default: - free(z->next_in); - return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z)); + return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z)); } } break; + case ZLIB_GZIP_TRAILER: + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; + return process_trailer(conn, zp); + case ZLIB_GZIP_INFLATING: default: /* Inflating stream state */ - z->next_in = (Bytef *)k->str; - z->avail_in = (uInt)nread; + z->next_in = (Bytef *) buf; + z->avail_in = (uInt) nbytes; break; } @@ -415,17 +545,469 @@ Curl_unencode_gzip_write(struct connectdata *conn, } /* We've parsed the header, now uncompress the data */ - return inflate_stream(conn, k); + return inflate_stream(conn, writer, ZLIB_GZIP_INFLATING); #endif } +static void gzip_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + zlib_params *zp = (zlib_params *) &writer->params; + z_stream *z = &zp->z; /* zlib state structure */ + + exit_zlib(conn, z, &zp->zlib_init, CURLE_OK); +} + +static const content_encoding gzip_encoding = { + "gzip", + "x-gzip", + gzip_init_writer, + gzip_unencode_write, + gzip_close_writer, + sizeof(zlib_params) +}; + +#endif /* HAVE_LIBZ */ + + +#ifdef HAVE_BROTLI + +/* Writer parameters. */ +typedef struct { + BrotliDecoderState *br; /* State structure for brotli. */ +} brotli_params; + + +static CURLcode brotli_map_error(BrotliDecoderErrorCode be) +{ + switch(be) { + case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: + case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: + case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: + case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: + case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: + case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: + case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: + case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: + case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: + case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: + case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: + case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: + case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: + case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: +#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY + case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: +#endif +#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET + case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: +#endif + case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: + return CURLE_BAD_CONTENT_ENCODING; + case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: + case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: + case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: + case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: + case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: + case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: + return CURLE_OUT_OF_MEMORY; + default: + break; + } + return CURLE_WRITE_ERROR; +} + +static CURLcode brotli_init_writer(struct connectdata *conn, + contenc_writer *writer) +{ + brotli_params *bp = (brotli_params *) &writer->params; + + (void) conn; + + if(!writer->downstream) + return CURLE_WRITE_ERROR; + + bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); + return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; +} + +static CURLcode brotli_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) +{ + brotli_params *bp = (brotli_params *) &writer->params; + const uint8_t *src = (const uint8_t *) buf; + char *decomp; + uint8_t *dst; + size_t dstleft; + CURLcode result = CURLE_OK; + BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + if(!bp->br) + return CURLE_WRITE_ERROR; /* Stream already ended. */ + + decomp = malloc(DSIZ); + if(!decomp) + return CURLE_OUT_OF_MEMORY; + + while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && + result == CURLE_OK) { + dst = (uint8_t *) decomp; + dstleft = DSIZ; + r = BrotliDecoderDecompressStream(bp->br, + &nbytes, &src, &dstleft, &dst, NULL); + result = Curl_unencode_write(conn, writer->downstream, + decomp, DSIZ - dstleft); + if(result) + break; + switch(r) { + case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: + case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: + break; + case BROTLI_DECODER_RESULT_SUCCESS: + BrotliDecoderDestroyInstance(bp->br); + bp->br = NULL; + if(nbytes) + result = CURLE_WRITE_ERROR; + break; + default: + result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br)); + break; + } + } + free(decomp); + return result; +} + +static void brotli_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + brotli_params *bp = (brotli_params *) &writer->params; + + (void) conn; + + if(bp->br) { + BrotliDecoderDestroyInstance(bp->br); + bp->br = NULL; + } +} + +static const content_encoding brotli_encoding = { + "br", + NULL, + brotli_init_writer, + brotli_unencode_write, + brotli_close_writer, + sizeof(brotli_params) +}; +#endif + + +/* Identity handler. */ +static CURLcode identity_init_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; +} + +static CURLcode identity_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) +{ + return Curl_unencode_write(conn, writer->downstream, buf, nbytes); +} + +static void identity_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + (void) writer; +} + +static const content_encoding identity_encoding = { + "identity", + NULL, + identity_init_writer, + identity_unencode_write, + identity_close_writer, + 0 +}; + + +/* supported content encodings table. */ +static const content_encoding * const encodings[] = { + &identity_encoding, +#ifdef HAVE_LIBZ + &deflate_encoding, + &gzip_encoding, +#endif +#ifdef HAVE_BROTLI + &brotli_encoding, +#endif + NULL +}; + + +/* Return a list of comma-separated names of supported encodings. */ +char *Curl_all_content_encodings(void) +{ + size_t len = 0; + const content_encoding * const *cep; + const content_encoding *ce; + char *ace; + char *p; + + for(cep = encodings; *cep; cep++) { + ce = *cep; + if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) + len += strlen(ce->name) + 2; + } + + if(!len) + return strdup(CONTENT_ENCODING_DEFAULT); + + ace = malloc(len); + if(ace) { + p = ace; + for(cep = encodings; *cep; cep++) { + ce = *cep; + if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { + strcpy(p, ce->name); + p += strlen(p); + *p++ = ','; + *p++ = ' '; + } + } + p[-2] = '\0'; + } + + return ace; +} + + +/* Real client writer: no downstream. */ +static CURLcode client_init_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; +} + +static CURLcode client_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) +{ + struct Curl_easy *data = conn->data; + struct SingleRequest *k = &data->req; + + (void) writer; + + if(!nbytes || k->ignorebody) + return CURLE_OK; + + return Curl_client_write(conn, CLIENTWRITE_BODY, (char *) buf, nbytes); +} + +static void client_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + (void) writer; +} + +static const content_encoding client_encoding = { + NULL, + NULL, + client_init_writer, + client_unencode_write, + client_close_writer, + 0 +}; + + +/* Deferred error dummy writer. */ +static CURLcode error_init_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; +} + +static CURLcode error_unencode_write(struct connectdata *conn, + contenc_writer *writer, + const char *buf, size_t nbytes) +{ + char *all = Curl_all_content_encodings(); + + (void) writer; + (void) buf; + (void) nbytes; + + if(!all) + return CURLE_OUT_OF_MEMORY; + failf(conn->data, "Unrecognized content encoding type. " + "libcurl understands %s content encodings.", all); + free(all); + return CURLE_BAD_CONTENT_ENCODING; +} + +static void error_close_writer(struct connectdata *conn, + contenc_writer *writer) +{ + (void) conn; + (void) writer; +} + +static const content_encoding error_encoding = { + NULL, + NULL, + error_init_writer, + error_unencode_write, + error_close_writer, + 0 +}; + +/* Create an unencoding writer stage using the given handler. */ +static contenc_writer *new_unencoding_writer(struct connectdata *conn, + const content_encoding *handler, + contenc_writer *downstream) +{ + size_t sz = offsetof(contenc_writer, params) + handler->paramsize; + contenc_writer *writer = (contenc_writer *) malloc(sz); + + if(writer) { + memset(writer, 0, sz); + writer->handler = handler; + writer->downstream = downstream; + if(handler->init_writer(conn, writer)) { + free(writer); + writer = NULL; + } + } + + return writer; +} + +/* Write data using an unencoding writer stack. */ +CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, + const char *buf, size_t nbytes) +{ + if(!nbytes) + return CURLE_OK; + return writer->handler->unencode_write(conn, writer, buf, nbytes); +} + +/* Close and clean-up the connection's writer stack. */ void Curl_unencode_cleanup(struct connectdata *conn) { struct Curl_easy *data = conn->data; struct SingleRequest *k = &data->req; - z_stream *z = &k->z; - if(k->zlib_init != ZLIB_UNINIT) - (void) exit_zlib(z, &k->zlib_init, CURLE_OK); + contenc_writer *writer = k->writer_stack; + + while(writer) { + k->writer_stack = writer->downstream; + writer->handler->close_writer(conn, writer); + free(writer); + writer = k->writer_stack; + } } -#endif /* HAVE_LIBZ */ +/* Find the content encoding by name. */ +static const content_encoding *find_encoding(const char *name, size_t len) +{ + const content_encoding * const *cep; + const content_encoding *ce; + + for(cep = encodings; *cep; cep++) { + ce = *cep; + if((strncasecompare(name, ce->name, len) && !ce->name[len]) || + (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) + return ce; + } + return NULL; +} + +/* Set-up the unencoding stack from the Content-Encoding header value. + * See RFC 7231 section 3.1.2.2. */ +CURLcode Curl_build_unencoding_stack(struct connectdata *conn, + const char *enclist, int maybechunked) +{ + struct Curl_easy *data = conn->data; + struct SingleRequest *k = &data->req; + + do { + const char *name; + size_t namelen; + + /* Parse a single encoding name. */ + while(ISSPACE(*enclist) || *enclist == ',') + enclist++; + + name = enclist; + + for(namelen = 0; *enclist && *enclist != ','; enclist++) + if(!ISSPACE(*enclist)) + namelen = enclist - name + 1; + + /* Special case: chunked encoding is handled at the reader level. */ + if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) { + k->chunk = TRUE; /* chunks coming our way. */ + Curl_httpchunk_init(conn); /* init our chunky engine. */ + } + else if(namelen) { + const content_encoding *encoding = find_encoding(name, namelen); + contenc_writer *writer; + + if(!k->writer_stack) { + k->writer_stack = new_unencoding_writer(conn, &client_encoding, NULL); + + if(!k->writer_stack) + return CURLE_OUT_OF_MEMORY; + } + + if(!encoding) + encoding = &error_encoding; /* Defer error at stack use. */ + + /* Stack the unencoding stage. */ + writer = new_unencoding_writer(conn, encoding, k->writer_stack); + if(!writer) + return CURLE_OUT_OF_MEMORY; + k->writer_stack = writer; + } + } while(*enclist); + + return CURLE_OK; +} + +#else +/* Stubs for builds without HTTP. */ +CURLcode Curl_build_unencoding_stack(struct connectdata *conn, + const char *enclist, int maybechunked) +{ + (void) conn; + (void) enclist; + (void) maybechunked; + return CURLE_NOT_BUILT_IN; +} + +CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, + const char *buf, size_t nbytes) +{ + (void) conn; + (void) writer; + (void) buf; + (void) nbytes; + return CURLE_NOT_BUILT_IN; +} + +void Curl_unencode_cleanup(struct connectdata *conn) +{ + (void) conn; +} + +char *Curl_all_content_encodings(void) +{ + return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ +} + +#endif /* CURL_DISABLE_HTTP */ diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h index 3fadd28..4cd52be 100644 --- a/Utilities/cmcurl/lib/content_encoding.h +++ b/Utilities/cmcurl/lib/content_encoding.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,26 +23,33 @@ ***************************************************************************/ #include "curl_setup.h" -/* - * Comma-separated list all supported Content-Encodings ('identity' is implied) - */ -#ifdef HAVE_LIBZ -#define ALL_CONTENT_ENCODINGS "deflate, gzip" -/* force a cleanup */ -void Curl_unencode_cleanup(struct connectdata *conn); -#else -#define ALL_CONTENT_ENCODINGS "identity" -#define Curl_unencode_cleanup(x) Curl_nop_stmt -#endif +/* Decoding writer. */ +typedef struct contenc_writer_s contenc_writer; +typedef struct content_encoding_s content_encoding; + +struct contenc_writer_s { + const content_encoding *handler; /* Encoding handler. */ + contenc_writer *downstream; /* Downstream writer. */ + void *params; /* Encoding-specific storage (variable length). */ +}; -CURLcode Curl_unencode_deflate_write(struct connectdata *conn, - struct SingleRequest *req, - ssize_t nread); +/* Content encoding writer. */ +struct content_encoding_s { + const char *name; /* Encoding name. */ + const char *alias; /* Encoding name alias. */ + CURLcode (*init_writer)(struct connectdata *conn, contenc_writer *writer); + CURLcode (*unencode_write)(struct connectdata *conn, contenc_writer *writer, + const char *buf, size_t nbytes); + void (*close_writer)(struct connectdata *conn, contenc_writer *writer); + size_t paramsize; +}; -CURLcode -Curl_unencode_gzip_write(struct connectdata *conn, - struct SingleRequest *k, - ssize_t nread); +CURLcode Curl_build_unencoding_stack(struct connectdata *conn, + const char *enclist, int maybechunked); +CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer, + const char *buf, size_t nbytes); +void Curl_unencode_cleanup(struct connectdata *conn); +char *Curl_all_content_encodings(void); #endif /* HEADER_CURL_CONTENT_ENCODING_H */ diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index 7fb596a..5f0f145 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -309,7 +309,7 @@ static void remove_expired(struct CookieInfo *cookies) while(co) { nx = co->next; if(co->expires && co->expires < now) { - if(co == cookies->cookies) { + if(!pv) { cookies->cookies = co->next; } else { diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index 6eb28bb..ec76f75 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c @@ -27,6 +27,9 @@ #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif +#ifdef HAVE_NETINET_IN6_H +# include <netinet/in6.h> +#endif #ifdef HAVE_NETDB_H # include <netdb.h> #endif diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index abe9ffe..1d1e3d7 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -389,9 +389,6 @@ /* if zlib is available */ #cmakedefine HAVE_LIBZ 1 -/* Define to 1 if you have the <limits.h> header file. */ -#cmakedefine HAVE_LIMITS_H 1 - /* if your compiler supports LL */ #cmakedefine HAVE_LL 1 @@ -998,3 +995,6 @@ typedef int ssize_t; # endif #endif + +/* Define to 1 if you have the mach_absolute_time function. */ +#cmakedefine HAVE_MACH_ABSOLUTE_TIME 1 diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c index 631268b..f33bba1 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.c +++ b/Utilities/cmcurl/lib/curl_fnmatch.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -133,6 +133,9 @@ static int setcharset(unsigned char **p, unsigned char *charset) unsigned char c; for(;;) { c = **p; + if(!c) + return SETCHARSET_FAIL; + switch(state) { case CURLFNM_SCHS_DEFAULT: if(ISALNUM(c)) { /* ASCII value */ @@ -196,9 +199,6 @@ static int setcharset(unsigned char **p, unsigned char *charset) else return SETCHARSET_FAIL; } - else if(c == '\0') { - return SETCHARSET_FAIL; - } else { charset[c] = 1; (*p)++; @@ -235,15 +235,10 @@ static int setcharset(unsigned char **p, unsigned char *charset) return SETCHARSET_FAIL; break; case CURLFNM_SCHS_MAYRANGE2: - if(c == '\\') { - c = *(++(*p)); - if(!ISPRINT(c)) - return SETCHARSET_FAIL; - } if(c == ']') { return SETCHARSET_OK; } - if(c == '\\') { + else if(c == '\\') { c = *(++(*p)); if(ISPRINT(c)) { charset[c] = 1; @@ -253,7 +248,7 @@ static int setcharset(unsigned char **p, unsigned char *charset) else return SETCHARSET_FAIL; } - if(c >= rangestart) { + else if(c >= rangestart) { if((ISLOWER(c) && ISLOWER(rangestart)) || (ISDIGIT(c) && ISDIGIT(rangestart)) || (ISUPPER(c) && ISUPPER(rangestart))) { @@ -267,6 +262,8 @@ static int setcharset(unsigned char **p, unsigned char *charset) else return SETCHARSET_FAIL; } + else + return SETCHARSET_FAIL; break; case CURLFNM_SCHS_RIGHTBR: if(c == '[') { @@ -277,9 +274,6 @@ static int setcharset(unsigned char **p, unsigned char *charset) else if(c == ']') { return SETCHARSET_OK; } - else if(c == '\0') { - return SETCHARSET_FAIL; - } else if(ISPRINT(c)) { charset[c] = 1; (*p)++; @@ -307,7 +301,8 @@ fail: return SETCHARSET_FAIL; } -static int loop(const unsigned char *pattern, const unsigned char *string) +static int loop(const unsigned char *pattern, const unsigned char *string, + int maxstars) { loop_state state = CURLFNM_LOOP_DEFAULT; unsigned char *p = (unsigned char *)pattern; @@ -319,11 +314,14 @@ static int loop(const unsigned char *pattern, const unsigned char *string) switch(state) { case CURLFNM_LOOP_DEFAULT: if(*p == '*') { + if(!maxstars) + return CURL_FNMATCH_NOMATCH; while(*(p + 1) == '*') /* eliminate multiple stars */ p++; if(*s == '\0' && *(p + 1) == '\0') return CURL_FNMATCH_MATCH; - rc = loop(p + 1, s); /* *.txt matches .txt <=> .txt matches .txt */ + rc = loop(p + 1, s, maxstars - 1); /* *.txt matches .txt <=> + .txt matches .txt */ if(rc == CURL_FNMATCH_MATCH) return CURL_FNMATCH_MATCH; if(*s) /* let the star eat up one character */ @@ -382,7 +380,9 @@ static int loop(const unsigned char *pattern, const unsigned char *string) if(found) { p = pp + 1; - s++; + if(*s) + /* don't advance if we're matching on an empty string */ + s++; memset(charset, 0, CURLFNM_CHSET_SIZE); } else @@ -420,5 +420,5 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) if(!pattern || !string) { return CURL_FNMATCH_FAIL; } - return loop((unsigned char *)pattern, (unsigned char *)string); + return loop((unsigned char *)pattern, (unsigned char *)string, 5); } diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 5154949..e896276 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -557,7 +557,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data, unsigned char *ntbuffer /* 21 bytes */) { size_t len = strlen(password); - unsigned char *pw = malloc(len * 2); + unsigned char *pw = len ? malloc(len * 2) : strdup(""); CURLcode result; if(!pw) return CURLE_OUT_OF_MEMORY; @@ -646,6 +646,15 @@ CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, return CURLE_OK; } +#ifndef SIZE_T_MAX +/* some limits.h headers have this defined, some don't */ +#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) +#define SIZE_T_MAX 18446744073709551615U +#else +#define SIZE_T_MAX 4294967295U +#endif +#endif + /* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode * (uppercase UserName + Domain) as the data */ @@ -655,10 +664,20 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, unsigned char *ntlmv2hash) { /* Unicode representation */ - size_t identity_len = (userlen + domlen) * 2; - unsigned char *identity = malloc(identity_len); + size_t identity_len; + unsigned char *identity; CURLcode result = CURLE_OK; + /* we do the length checks below separately to avoid integer overflow risk + on extreme data lengths */ + if((userlen > SIZE_T_MAX/2) || + (domlen > SIZE_T_MAX/2) || + ((userlen + domlen) > SIZE_T_MAX/2)) + return CURLE_OUT_OF_MEMORY; + + identity_len = (userlen + domlen) * 2; + identity = malloc(identity_len); + if(!identity) return CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c new file mode 100644 index 0000000..e843dea --- /dev/null +++ b/Utilities/cmcurl/lib/curl_path.c @@ -0,0 +1,195 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include <curl/curl.h> +#include "curl_memory.h" +#include "curl_path.h" +#include "escape.h" +#include "memdebug.h" + +/* figure out the path to work with in this particular request */ +CURLcode Curl_getworkingpath(struct connectdata *conn, + char *homedir, /* when SFTP is used */ + char **path) /* returns the allocated + real path to work with */ +{ + struct Curl_easy *data = conn->data; + char *real_path = NULL; + char *working_path; + size_t working_path_len; + CURLcode result = + Curl_urldecode(data, data->state.path, 0, &working_path, + &working_path_len, FALSE); + if(result) + return result; + + /* Check for /~/, indicating relative to the user's home directory */ + if(conn->handler->protocol & CURLPROTO_SCP) { + real_path = malloc(working_path_len + 1); + if(real_path == NULL) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) + /* It is referenced to the home directory, so strip the leading '/~/' */ + memcpy(real_path, working_path + 3, 4 + working_path_len-3); + else + memcpy(real_path, working_path, 1 + working_path_len); + } + else if(conn->handler->protocol & CURLPROTO_SFTP) { + if((working_path_len > 1) && (working_path[1] == '~')) { + size_t homelen = strlen(homedir); + real_path = malloc(homelen + working_path_len + 1); + if(real_path == NULL) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + /* It is referenced to the home directory, so strip the + leading '/' */ + memcpy(real_path, homedir, homelen); + real_path[homelen] = '/'; + real_path[homelen + 1] = '\0'; + if(working_path_len > 3) { + memcpy(real_path + homelen + 1, working_path + 3, + 1 + working_path_len -3); + } + } + else { + real_path = malloc(working_path_len + 1); + if(real_path == NULL) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + memcpy(real_path, working_path, 1 + working_path_len); + } + } + + free(working_path); + + /* store the pointer for the caller to receive */ + *path = real_path; + + return CURLE_OK; +} + +/* The get_pathname() function is being borrowed from OpenSSH sftp.c + version 4.6p1. */ +/* + * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) +{ + const char *cp = *cpp, *end; + char quot; + unsigned int i, j; + size_t fullPathLength, pathLength; + bool relativePath = false; + static const char WHITESPACE[] = " \t\r\n"; + + if(!*cp) { + *cpp = NULL; + *path = NULL; + return CURLE_QUOTE_ERROR; + } + /* Ignore leading whitespace */ + cp += strspn(cp, WHITESPACE); + /* Allocate enough space for home directory and filename + separator */ + fullPathLength = strlen(cp) + strlen(homedir) + 2; + *path = malloc(fullPathLength); + if(*path == NULL) + return CURLE_OUT_OF_MEMORY; + + /* Check for quoted filenames */ + if(*cp == '\"' || *cp == '\'') { + quot = *cp++; + + /* Search for terminating quote, unescape some chars */ + for(i = j = 0; i <= strlen(cp); i++) { + if(cp[i] == quot) { /* Found quote */ + i++; + (*path)[j] = '\0'; + break; + } + if(cp[i] == '\0') { /* End of string */ + /*error("Unterminated quote");*/ + goto fail; + } + if(cp[i] == '\\') { /* Escaped characters */ + i++; + if(cp[i] != '\'' && cp[i] != '\"' && + cp[i] != '\\') { + /*error("Bad escaped character '\\%c'", + cp[i]);*/ + goto fail; + } + } + (*path)[j++] = cp[i]; + } + + if(j == 0) { + /*error("Empty quotes");*/ + goto fail; + } + *cpp = cp + i + strspn(cp + i, WHITESPACE); + } + else { + /* Read to end of filename - either to white space or terminator */ + end = strpbrk(cp, WHITESPACE); + if(end == NULL) + end = strchr(cp, '\0'); + /* return pointer to second parameter if it exists */ + *cpp = end + strspn(end, WHITESPACE); + pathLength = 0; + relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/'); + /* Handling for relative path - prepend home directory */ + if(relativePath) { + strcpy(*path, homedir); + pathLength = strlen(homedir); + (*path)[pathLength++] = '/'; + (*path)[pathLength] = '\0'; + cp += 3; + } + /* Copy path name up until first "white space" */ + memcpy(&(*path)[pathLength], cp, (int)(end - cp)); + pathLength += (int)(end - cp); + (*path)[pathLength] = '\0'; + } + return CURLE_OK; + + fail: + Curl_safefree(*path); + return CURLE_QUOTE_ERROR; +} diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h new file mode 100644 index 0000000..f9d4327 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_path.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" +#include <curl/curl.h> +#include "urldata.h" + +#ifdef WIN32 +# undef PATH_MAX +# define PATH_MAX MAX_PATH +# ifndef R_OK +# define R_OK 4 +# endif +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 /* just an extra precaution since there are systems that + have their definition hidden well */ +#endif + +CURLcode Curl_getworkingpath(struct connectdata *conn, + char *homedir, + char **path); + +CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir); diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 5880dc0..16e1ccd 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -431,6 +431,14 @@ # endif #endif +#if (SIZEOF_CURL_OFF_T == 4) +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) +#else + /* assume CURL_SIZEOF_CURL_OFF_T == 8 */ +# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) +#endif +#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) + /* * Arg 2 type for gethostname in case it hasn't been defined in config file. */ @@ -766,9 +774,10 @@ endings either CRLF or LF so 't' is appropriate. /* Detect Windows App environment which has a restricted access * to the Win32 APIs. */ -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602) +# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ + defined(WINAPI_FAMILY) # include <winapifamily.h> -# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define CURL_WINDOWS_APP # endif diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h new file mode 100644 index 0000000..6db4b04 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURL_SHA256_H +#define HEADER_CURL_SHA256_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2010, Florin Petriuc, <petriuc.florin@gmail.com> + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#ifndef CURL_DISABLE_CRYPTO_AUTH + +void Curl_sha256it(unsigned char *outbuffer, + const unsigned char *input); + +#endif + +#endif /* HEADER_CURL_SHA256_H */ diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h index 6168dc1..6e41826 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -42,16 +42,6 @@ curl_off_t number from a given string. */ -#include "timeval.h" -/* - "timeval.h" sets up a 'struct timeval' even for platforms that otherwise - don't have one and has protos for these functions: - - curlx_tvnow() - curlx_tvdiff() - curlx_tvdiff_secs() -*/ - #include "nonblock.h" /* "nonblock.h" provides curlx_nonblock() */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 5328f9c..edc716d 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -65,13 +65,15 @@ #include "sendf.h" /* for failf function prototype */ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" +#include "mime.h" #include "amigaos.h" #include "non-ascii.h" #include "warnless.h" -#include "conncache.h" #include "multiif.h" #include "sigpipe.h" #include "ssh.h" +#include "setopt.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -214,11 +216,10 @@ static CURLcode global_init(long flags, bool memoryfuncs) #endif } - if(flags & CURL_GLOBAL_SSL) - if(!Curl_ssl_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); - return CURLE_FAILED_INIT; - } + if(!Curl_ssl_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); + return CURLE_FAILED_INIT; + } if(flags & CURL_GLOBAL_WIN32) if(win32_init()) { @@ -253,6 +254,13 @@ static CURLcode global_init(long flags, bool memoryfuncs) } #endif +#if defined(USE_LIBSSH) + if(ssh_init()) { + DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); + return CURLE_FAILED_INIT; + } +#endif + if(flags & CURL_GLOBAL_ACK_EINTR) Curl_ack_eintr = 1; @@ -318,10 +326,7 @@ void curl_global_cleanup(void) return; Curl_global_host_cache_dtor(); - - if(init_flags & CURL_GLOBAL_SSL) - Curl_ssl_cleanup(); - + Curl_ssl_cleanup(); Curl_resolver_global_cleanup(); if(init_flags & CURL_GLOBAL_WIN32) @@ -333,6 +338,10 @@ void curl_global_cleanup(void) (void)libssh2_exit(); #endif +#if defined(USE_LIBSSH) + (void)ssh_finalize(); +#endif + init_flags = 0; } @@ -365,28 +374,6 @@ struct Curl_easy *curl_easy_init(void) return data; } -/* - * curl_easy_setopt() is the external interface for setting options on an - * easy handle. - */ - -#undef curl_easy_setopt -CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) -{ - va_list arg; - CURLcode result; - - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - va_start(arg, tag); - - result = Curl_setopt(data, tag, arg); - - va_end(arg); - return result; -} - #ifdef CURLDEBUG struct socketmonitor { @@ -586,12 +573,12 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) } /* get the time stamp to use to figure out how long poll takes */ - before = curlx_tvnow(); + before = Curl_now(); /* wait for activity or timeout */ pollrc = Curl_poll(fds, numfds, (int)ev->ms); - after = curlx_tvnow(); + after = Curl_now(); ev->msbump = FALSE; /* reset here */ @@ -619,7 +606,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) /* If nothing updated the timeout, we decrease it by the spent time. * If it was updated, it has the new timeout time stored already. */ - time_t timediff = curlx_tvdiff(after, before); + timediff_t timediff = Curl_timediff(after, before); if(timediff > 0) { if(timediff > ev->ms) ev->ms = 0; @@ -680,17 +667,17 @@ static CURLcode easy_transfer(struct Curl_multi *multi) int still_running = 0; int rc; - before = curlx_tvnow(); + before = Curl_now(); mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc); if(!mcode) { if(!rc) { - struct curltime after = curlx_tvnow(); + struct curltime after = Curl_now(); /* If it returns without any filedescriptor instantly, we need to avoid busy-looping during periods where it has nothing particular to wait for */ - if(curlx_tvdiff(after, before) <= 10) { + if(Curl_timediff(after, before) <= 10) { without_fds++; if(without_fds > 2) { int sleep_ms = without_fds < 10 ? (1 << (without_fds - 1)) : 1000; @@ -861,6 +848,44 @@ CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) return result; } +static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) +{ + CURLcode result = CURLE_OK; + enum dupstring i; + + /* Copy src->set into dst->set first, then deal with the strings + afterwards */ + dst->set = src->set; + Curl_mime_initpart(&dst->set.mimepost, dst); + + /* clear all string pointers first */ + memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); + + /* duplicate all strings */ + for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { + result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); + if(result) + return result; + } + + /* duplicate memory areas pointed to */ + i = STRING_COPYPOSTFIELDS; + if(src->set.postfieldsize && src->set.str[i]) { + /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ + dst->set.str[i] = Curl_memdup(src->set.str[i], + curlx_sotouz(src->set.postfieldsize)); + if(!dst->set.str[i]) + return CURLE_OUT_OF_MEMORY; + /* point to the new copy */ + dst->set.postfields = dst->set.str[i]; + } + + /* Duplicate mime data. */ + result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); + + return result; +} + /* * curl_easy_duphandle() is an external interface to allow duplication of a * given input easy handle. The returned handle will be a new working handle @@ -888,7 +913,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.headersize = HEADERSIZE; /* copy all userdefined values */ - if(Curl_dupset(outcurl, data)) + if(dupset(outcurl, data)) goto fail; /* the connection cache is setup on demand */ @@ -978,7 +1003,7 @@ void curl_easy_reset(struct Curl_easy *data) /* zero out UserDefined data: */ Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); - (void)Curl_init_userdefined(&data->set); + (void)Curl_init_userdefined(data); /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); @@ -1025,6 +1050,8 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) unsigned int i; unsigned int count = data->state.tempcount; struct tempbuf writebuf[3]; /* there can only be three */ + struct connectdata *conn = data->easy_conn; + struct Curl_easy *saved_data = NULL; /* copy the structs to allow for immediate re-pausing */ for(i = 0; i < data->state.tempcount; i++) { @@ -1033,16 +1060,27 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) } data->state.tempcount = 0; + /* set the connection's current owner */ + if(conn->data != data) { + saved_data = conn->data; + conn->data = data; + } + for(i = 0; i < count; i++) { /* even if one function returns error, this loops through and frees all buffers */ if(!result) - result = Curl_client_chop_write(data->easy_conn, + result = Curl_client_chop_write(conn, writebuf[i].type, writebuf[i].buf, writebuf[i].len); free(writebuf[i].buf); } + + /* recover previous owner of the connection */ + if(saved_data) + conn->data = saved_data; + if(result) return result; } diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index 7cfdab1..0bbc0e1 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -404,7 +404,7 @@ static CURLcode file_upload(struct connectdata *conn) if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, Curl_tvnow()); + result = Curl_speedcheck(data, Curl_now()); } if(!result && Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; @@ -589,7 +589,7 @@ static CURLcode file_do(struct connectdata *conn, bool *done) if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, Curl_tvnow()); + result = Curl_speedcheck(data, Curl_now()); } if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index 3568ac5..d0579c5 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -907,7 +907,7 @@ CURLcode Curl_getformdata(struct Curl_easy *data, result = curl_mime_headers(part, file->contentheader, 0); /* Set the content type. */ - if(!result &&file->contenttype) + if(!result && file->contenttype) result = curl_mime_type(part, file->contenttype); /* Set field name. */ diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 84ae37b..d3b0220 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -182,7 +182,8 @@ const struct Curl_handler Curl_handler_ftp = { PORT_FTP, /* defport */ CURLPROTO_FTP, /* protocol */ PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | - PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | + PROTOPT_WILDCARD /* flags */ }; @@ -210,7 +211,7 @@ const struct Curl_handler Curl_handler_ftps = { PORT_FTPS, /* defport */ CURLPROTO_FTPS, /* protocol */ PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | - PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */ + PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */ }; #endif @@ -332,16 +333,16 @@ static CURLcode AcceptServerConnect(struct connectdata *conn) * Curl_pgrsTime(..., TIMER_STARTACCEPT); * */ -static time_t ftp_timeleft_accept(struct Curl_easy *data) +static timediff_t ftp_timeleft_accept(struct Curl_easy *data) { - time_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; - time_t other; + timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; + timediff_t other; struct curltime now; if(data->set.accepttimeout > 0) timeout_ms = data->set.accepttimeout; - now = Curl_tvnow(); + now = Curl_now(); /* check if the generic timeout possibly is set shorter */ other = Curl_timeleft(data, &now, FALSE); @@ -351,7 +352,7 @@ static time_t ftp_timeleft_accept(struct Curl_easy *data) timeout_ms = other; else { /* subtract elapsed time */ - timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata); + timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata); if(!timeout_ms) /* avoid returning 0 as that means no timeout! */ return -1; @@ -1457,25 +1458,22 @@ static CURLcode ftp_state_list(struct connectdata *conn) then just do LIST (in that case: nothing to do here) */ char *cmd, *lstArg, *slashPos; + const char *inpath = data->state.path; lstArg = NULL; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && - data->state.path && - data->state.path[0] && - strchr(data->state.path, '/')) { - - lstArg = strdup(data->state.path); - if(!lstArg) - return CURLE_OUT_OF_MEMORY; + inpath && inpath[0] && strchr(inpath, '/')) { + size_t n = strlen(inpath); /* Check if path does not end with /, as then we cut off the file part */ - if(lstArg[strlen(lstArg) - 1] != '/') { - + if(inpath[n - 1] != '/') { /* chop off the file part if format is dir/dir/file */ - slashPos = strrchr(lstArg, '/'); - if(slashPos) - *(slashPos + 1) = '\0'; + slashPos = strrchr(inpath, '/'); + n = slashPos - inpath; } + result = Curl_urldecode(data, inpath, n, &lstArg, NULL, FALSE); + if(result) + return result; } cmd = aprintf("%s%s%s", @@ -1877,8 +1875,8 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, else if((ftpc->count1 == 1) && (ftpcode == 227)) { /* positive PASV response */ - int ip[4]; - int port[2]; + unsigned int ip[4]; + unsigned int port[2]; /* * Scan for a sequence of six comma-separated numbers and use them as @@ -1890,14 +1888,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, * "227 Entering passive mode. 127,0,0,1,4,51" */ while(*str) { - if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d", + if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u", &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1])) break; str++; } - if(!*str) { + if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) || + (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) { failf(data, "Couldn't interpret the 227-response"); return CURLE_FTP_WEIRD_227_FORMAT; } @@ -2419,8 +2418,8 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn, char *bytes; char *buf = data->state.buffer; bytes = strstr(buf, " bytes"); - if(bytes--) { - long in = (long)(bytes-buf); + if(bytes) { + long in = (long)(--bytes-buf); /* this is a hint there is size information in there! ;-) */ while(--in) { /* scan for the left parenthesis and break there */ @@ -3179,7 +3178,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, /* now store a copy of the directory we are in */ free(ftpc->prevpath); - if(data->set.wildcardmatch) { + if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { data->set.chunk_end(data->wildcard.customptr); } @@ -3263,7 +3262,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, long old_time = pp->response_time; pp->response_time = 60*1000; /* give it only a minute for now */ - pp->response = Curl_tvnow(); /* timeout relative now */ + pp->response = Curl_now(); /* timeout relative now */ result = Curl_GetFTPResponse(&nread, conn, &ftpcode); @@ -3383,7 +3382,7 @@ CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) PPSENDF(&conn->proto.ftpc.pp, "%s", cmd); - pp->response = Curl_tvnow(); /* timeout relative now */ + pp->response = Curl_now(); /* timeout relative now */ result = Curl_GetFTPResponse(&nread, conn, &ftpcode); if(result) @@ -3965,7 +3964,7 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done) *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ - if(conn->data->set.wildcardmatch) { + if(conn->data->state.wildcardmatch) { result = wc_statemach(conn); if(conn->data->wildcard.state == CURLWC_SKIP || conn->data->wildcard.state == CURLWC_DONE) { diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index b2a8c0f..262ac03 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -264,16 +264,6 @@ static int ftp_pl_get_permission(const char *str) return permissions; } -static void PL_ERROR(struct connectdata *conn, CURLcode err) -{ - struct ftp_wc_tmpdata *tmpdata = conn->data->wildcard.tmp; - struct ftp_parselist_data *parser = tmpdata->parser; - if(parser->file_data) - Curl_fileinfo_dtor(NULL, parser->file_data); - parser->file_data = NULL; - parser->error = err; -} - static CURLcode ftp_pl_insert_finfo(struct connectdata *conn, struct fileinfo *infop) { @@ -338,6 +328,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, struct curl_fileinfo *finfo; unsigned long i = 0; CURLcode result; + size_t retsize = bufflen; if(parser->error) { /* error in previous call */ /* scenario: @@ -346,7 +337,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, * 3. (last) call => is skipped RIGHT HERE and the error is hadled later * in wc_statemach() */ - return bufflen; + goto fail; } if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { @@ -362,12 +353,12 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->file_data = Curl_fileinfo_alloc(); if(!parser->file_data) { parser->error = CURLE_OUT_OF_MEMORY; - return bufflen; + goto fail; } parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE); if(!parser->file_data->info.b_data) { - PL_ERROR(conn, CURLE_OUT_OF_MEMORY); - return bufflen; + parser->error = CURLE_OUT_OF_MEMORY; + goto fail; } parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE; parser->item_offset = 0; @@ -390,8 +381,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, Curl_fileinfo_dtor(NULL, parser->file_data); parser->file_data = NULL; parser->error = CURLE_OUT_OF_MEMORY; - PL_ERROR(conn, CURLE_OUT_OF_MEMORY); - return bufflen; + goto fail; } } @@ -429,15 +419,15 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, while(ISDIGIT(*endptr)) endptr++; if(*endptr != 0) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } parser->state.UNIX.main = PL_UNIX_FILETYPE; finfo->b_used = 0; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -470,8 +460,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, finfo->filetype = CURLFILETYPE_DOOR; break; default: - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } parser->state.UNIX.main = PL_UNIX_PERMISSION; parser->item_length = 0; @@ -481,21 +471,21 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length++; if(parser->item_length <= 9) { if(!strchr("rwx-tTsS", c)) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } else if(parser->item_length == 10) { unsigned int perm; if(c != ' ') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } finfo->b_data[10] = 0; /* terminate permissions */ perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); if(perm & FTP_LP_MALFORMATED_PERM) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; parser->file_data->info.perm = perm; @@ -516,8 +506,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -538,8 +528,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; } else if(c < '0' || c > '9') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -598,8 +588,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -623,8 +613,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, } } else if(!ISDIGIT(c)) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -639,8 +629,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -650,8 +640,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; } else if(!ISALNUM(c) && c != '.') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; case PL_UNIX_TIME_PREPART2: @@ -661,8 +651,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -672,8 +662,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; } else if(!ISALNUM(c) && c != '.') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; case PL_UNIX_TIME_PREPART3: @@ -683,8 +673,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } break; @@ -708,8 +698,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, } } else if(!ISALNUM(c) && c != '.' && c != ':') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -734,8 +724,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.main = PL_UNIX_FILETYPE; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } } break; @@ -746,13 +736,13 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.main = PL_UNIX_FILETYPE; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -772,8 +762,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; } else if(c == '\r' || c == '\n') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; case PL_UNIX_SYMLINK_PRETARGET1: @@ -782,8 +772,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; } else if(c == '\r' || c == '\n') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } else { parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; @@ -795,8 +785,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; } else if(c == '\r' || c == '\n') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } else { parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; @@ -813,8 +803,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_offset = 0; } else if(c == '\r' || c == '\n') { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } else { parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; @@ -827,8 +817,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length = 1; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; case PL_UNIX_SYMLINK_TARGET: @@ -841,8 +831,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->offsets.symlink_target = parser->item_offset; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } parser->state.UNIX.main = PL_UNIX_FILETYPE; } @@ -853,14 +843,14 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->offsets.symlink_target = parser->item_offset; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } parser->state.UNIX.main = PL_UNIX_FILETYPE; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -873,8 +863,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length++; if(parser->item_length < 9) { if(!strchr("0123456789-", c)) { /* only simple control */ - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } else if(parser->item_length == 9) { @@ -883,13 +873,13 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; case PL_WINNT_TIME: @@ -909,8 +899,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->item_length = 0; } else if(!strchr("APM0123456789:", c)) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -940,8 +930,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, if(curlx_strtoofft(finfo->b_data + parser->item_offset, &endptr, 10, &finfo->size)) { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } /* correct file type */ parser->file_data->info.filetype = CURLFILETYPE_FILE; @@ -976,8 +966,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->offsets.filename = parser->item_offset; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } parser->state.NT.main = PL_WINNT_DATE; parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; @@ -988,15 +978,15 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, parser->offsets.filename = parser->item_offset; result = ftp_pl_insert_finfo(conn, infop); if(result) { - PL_ERROR(conn, result); - return bufflen; + parser->error = result; + goto fail; } parser->state.NT.main = PL_WINNT_DATE; parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; } else { - PL_ERROR(conn, CURLE_FTP_BAD_FILE_LIST); - return bufflen; + parser->error = CURLE_FTP_BAD_FILE_LIST; + goto fail; } break; } @@ -1004,13 +994,22 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, } break; default: - return bufflen + 1; + retsize = bufflen + 1; + goto fail; } i++; } - return bufflen; +fail: + + /* Clean up any allocated memory. */ + if(parser->file_data) { + Curl_fileinfo_dtor(NULL, parser->file_data); + parser->file_data = NULL; + } + + return retsize; } #endif /* CURL_DISABLE_FTP */ diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 28bdf7a..7b6e856 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -22,6 +22,11 @@ #include "curl_setup.h" +/*********************************************************************** + * Only for builds using asynchronous name resolves + **********************************************************************/ +#ifdef CURLRES_ASYNCH + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -51,11 +56,6 @@ /* The last #include file should be: */ #include "memdebug.h" -/*********************************************************************** - * Only for builds using asynchronous name resolves - **********************************************************************/ -#ifdef CURLRES_ASYNCH - /* * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread() * or getaddrinfo_thread() when we got the name resolved (or not!). diff --git a/Utilities/cmcurl/lib/hostcheck.c b/Utilities/cmcurl/lib/hostcheck.c index 23dc3d2..37bcc12 100644 --- a/Utilities/cmcurl/lib/hostcheck.c +++ b/Utilities/cmcurl/lib/hostcheck.c @@ -31,6 +31,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif #include "hostcheck.h" #include "strcase.h" diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 1a18a3e..886aeec 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -25,6 +25,9 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -688,8 +691,8 @@ clean_up: the time we spent until now! */ if(prev_alarm) { /* there was an alarm() set before us, now put it back */ - unsigned long elapsed_secs = (unsigned long) (Curl_tvdiff(Curl_tvnow(), - conn->created) / 1000); + timediff_t elapsed_secs = Curl_timediff(Curl_now(), + conn->created) / 1000; /* the alarm period is counted in even number of seconds */ unsigned long alarm_set = prev_alarm - elapsed_secs; @@ -778,7 +781,6 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) { struct curl_slist *hostp; char hostname[256]; - char address[256]; int port; for(hostp = data->change.resolve; hostp; hostp = hostp->next) { @@ -820,6 +822,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_addrinfo *addr; char *entry_id; size_t entry_len; + char buffer[256]; + char *address = &buffer[0]; if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port, address)) { @@ -828,6 +832,16 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) continue; } + /* allow IP(v6) address within [brackets] */ + if(address[0] == '[') { + size_t alen = strlen(address); + if(address[alen-1] != ']') + /* it needs to also end with ] to be valid */ + continue; + address[alen-1] = 0; /* zero terminate there */ + address++; /* pass the open bracket */ + } + addr = Curl_str2addr(address, port); if(!addr) { infof(data, "Address in '%s' found illegal!\n", hostp->data); @@ -863,9 +877,12 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) dns->inuse--; } } - else + else { /* this is a duplicate, free it again */ + infof(data, "RESOLVE %s:%d is already cached, %s not stored!\n", + hostname, port, address); Curl_freeaddrinfo(addr); + } if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index 6a7c6e5..9d6f115 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c @@ -22,6 +22,11 @@ #include "curl_setup.h" +/*********************************************************************** + * Only for plain IPv4 builds + **********************************************************************/ +#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */ + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -53,10 +58,6 @@ #include "curl_memory.h" #include "memdebug.h" -/*********************************************************************** - * Only for plain IPv4 builds - **********************************************************************/ -#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */ /* * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've * been set and returns TRUE if they are OK. diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index edeebec..7c9988f 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -22,6 +22,11 @@ #include "curl_setup.h" +/*********************************************************************** + * Only for IPv6-enabled builds + **********************************************************************/ +#ifdef CURLRES_IPV6 + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -54,11 +59,6 @@ #include "curl_memory.h" #include "memdebug.h" -/*********************************************************************** - * Only for IPv6-enabled builds - **********************************************************************/ -#ifdef CURLRES_IPV6 - #if defined(CURLDEBUG) && defined(HAVE_GETNAMEINFO) /* These are strictly for memory tracing and are using the same style as the * family otherwise present in memdebug.c. I put these ones here since they diff --git a/Utilities/cmcurl/lib/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c index 1a95263..3de6746 100644 --- a/Utilities/cmcurl/lib/hostsyn.c +++ b/Utilities/cmcurl/lib/hostsyn.c @@ -22,6 +22,11 @@ #include "curl_setup.h" +/*********************************************************************** + * Only for builds using synchronous name resolves + **********************************************************************/ +#ifdef CURLRES_SYNCH + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -51,11 +56,6 @@ /* The last #include file should be: */ #include "memdebug.h" -/*********************************************************************** - * Only for builds using synchronous name resolves - **********************************************************************/ -#ifdef CURLRES_SYNCH - /* * Function provided by the resolver backend to set DNS servers to use. */ diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 38227eb..a500767 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -73,7 +73,6 @@ #include "http_proxy.h" #include "warnless.h" #include "non-ascii.h" -#include "conncache.h" #include "pipeline.h" #include "http2.h" #include "connect.h" @@ -715,7 +714,7 @@ Curl_http_output_auth(struct connectdata *conn, if(!data->state.this_is_a_follow || conn->bits.netrc || !data->state.first_host || - data->set.http_disable_hostname_check_before_authentication || + data->set.allow_auth_to_other_hosts || strcasecompare(data->state.first_host, conn->host.name)) { result = output_auth_headers(conn, authhost, request, path, FALSE); } @@ -1637,6 +1636,14 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, checkprefix("Transfer-Encoding:", headers->data)) /* HTTP/2 doesn't support chunked requests */ ; + else if(checkprefix("Authorization:", headers->data) && + /* be careful of sending this potentially sensitive header to + other hosts */ + (data->state.this_is_a_follow && + data->state.first_host && + !data->set.allow_auth_to_other_hosts && + !strcasecompare(data->state.first_host, conn->host.name))) + ; else { CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data); @@ -3104,7 +3111,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, !(conn->handler->protocol & CURLPROTO_RTSP) && data->set.httpreq != HTTPREQ_HEAD) { /* On HTTP 1.1, when connection is not to get closed, but no - Content-Length nor Content-Encoding chunked have been + Content-Length nor Transfer-Encoding chunked have been received, according to RFC2616 section 4.4 point 5, we assume that the server will close the connection to signal the end of the document. */ @@ -3387,12 +3394,14 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } else if(conn->handler->protocol & CURLPROTO_RTSP) { + char separator; nc = sscanf(HEADER1, - " RTSP/%d.%d %3d", + " RTSP/%1d.%1d%c%3d", &rtspversion_major, &conn->rtspversion, + &separator, &k->httpcode); - if(nc == 3) { + if((nc == 4) && (' ' == separator)) { conn->rtspversion += 10 * rtspversion_major; conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */ } @@ -3504,31 +3513,35 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(!k->ignorecl && !data->set.ignorecl && checkprefix("Content-Length:", k->p)) { curl_off_t contentlength; - if(!curlx_strtoofft(k->p + 15, NULL, 10, &contentlength)) { + CURLofft offt = curlx_strtoofft(k->p + 15, NULL, 10, &contentlength); + + if(offt == CURL_OFFT_OK) { if(data->set.max_filesize && contentlength > data->set.max_filesize) { failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; } - if(contentlength >= 0) { - k->size = contentlength; - k->maxdownload = k->size; - /* we set the progress download size already at this point - just to make it easier for apps/callbacks to extract this - info as soon as possible */ - Curl_pgrsSetDownloadSize(data, k->size); - } - else { - /* Negative Content-Length is really odd, and we know it - happens for example when older Apache servers send large - files */ - streamclose(conn, "negative content-length"); - infof(data, "Negative content-length: %" CURL_FORMAT_CURL_OFF_T - ", closing after transfer\n", contentlength); + k->size = contentlength; + k->maxdownload = k->size; + /* we set the progress download size already at this point + just to make it easier for apps/callbacks to extract this + info as soon as possible */ + Curl_pgrsSetDownloadSize(data, k->size); + } + else if(offt == CURL_OFFT_FLOW) { + /* out of range */ + if(data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; } + streamclose(conn, "overflow content-length"); + infof(data, "Overflow Content-Length: value!\n"); + } + else { + /* negative or just rubbish - bad HTTP */ + failf(data, "Invalid Content-Length: value"); + return CURLE_WEIRD_SERVER_REPLY; } - else - infof(data, "Illegal Content-Length: header\n"); } /* check for Content-Type: header lines to get the MIME-type */ else if(checkprefix("Content-Type:", k->p)) { @@ -3612,51 +3625,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * of chunks, and a chunk-data set to zero signals the * end-of-chunks. */ - char *start; - - /* Find the first non-space letter */ - start = k->p + 18; - - for(;;) { - /* skip whitespaces and commas */ - while(*start && (ISSPACE(*start) || (*start == ','))) - start++; - - if(checkprefix("chunked", start)) { - k->chunk = TRUE; /* chunks coming our way */ - - /* init our chunky engine */ - Curl_httpchunk_init(conn); - - start += 7; - } - - if(k->auto_decoding) - /* TODO: we only support the first mentioned compression for now */ - break; - - if(checkprefix("identity", start)) { - k->auto_decoding = IDENTITY; - start += 8; - } - else if(checkprefix("deflate", start)) { - k->auto_decoding = DEFLATE; - start += 7; - } - else if(checkprefix("gzip", start)) { - k->auto_decoding = GZIP; - start += 4; - } - else if(checkprefix("x-gzip", start)) { - k->auto_decoding = GZIP; - start += 6; - } - else - /* unknown! */ - break; - - } - + result = Curl_build_unencoding_stack(conn, k->p + 18, TRUE); + if(result) + return result; } else if(checkprefix("Content-Encoding:", k->p) && data->set.str[STRING_ENCODING]) { @@ -3667,21 +3638,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * 2616). zlib cannot handle compress. However, errors are * handled further down when the response body is processed */ - char *start; - - /* Find the first non-space letter */ - start = k->p + 17; - while(*start && ISSPACE(*start)) - start++; - - /* Record the content-encoding for later use */ - if(checkprefix("identity", start)) - k->auto_decoding = IDENTITY; - else if(checkprefix("deflate", start)) - k->auto_decoding = DEFLATE; - else if(checkprefix("gzip", start) - || checkprefix("x-gzip", start)) - k->auto_decoding = GZIP; + result = Curl_build_unencoding_stack(conn, k->p + 17, FALSE); + if(result) + return result; } else if(checkprefix("Content-Range:", k->p)) { /* Content-Range: bytes [num]- diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index 9a2a1dd..6992879 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -32,7 +32,6 @@ #include "curl_base64.h" #include "strcase.h" #include "multiif.h" -#include "conncache.h" #include "url.h" #include "connect.h" #include "strtoofft.h" @@ -926,8 +925,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is trailer fields. */ - /* 3 is for ":" and "\r\n". */ - uint32_t n = (uint32_t)(namelen + valuelen + 3); + /* 4 is for ": " and "\r\n". */ + uint32_t n = (uint32_t)(namelen + valuelen + 4); DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen, value)); @@ -1184,14 +1183,17 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, httpc->local_settings_num); if(!binlen) { failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); + Curl_add_buffer_free(req); return CURLE_FAILED_INIT; } conn->proto.httpc.binlen = binlen; result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen, &base64, &blen); - if(result) + if(result) { + Curl_add_buffer_free(req); return result; + } result = Curl_add_bufferf(req, "Connection: Upgrade, HTTP2-Settings\r\n" @@ -1846,9 +1848,6 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, goto fail; } - hdbuf = end + 1; - - end = line_end; nva[2].name = (unsigned char *)":scheme"; nva[2].namelen = strlen((char *)nva[2].name); if(conn->handler->flags & PROTOPT_SSL) diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 92d7731..1616429 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -187,49 +187,17 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize); /* Write the data portion available */ -#ifdef HAVE_LIBZ - switch(conn->data->set.http_ce_skip? - IDENTITY : data->req.auto_decoding) { - case IDENTITY: -#endif - if(!k->ignorebody) { - if(!data->set.http_te_skip) - result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, - piece); - else - result = CURLE_OK; - } -#ifdef HAVE_LIBZ - break; - - case DEFLATE: - /* update data->req.keep.str to point to the chunk data. */ - data->req.str = datap; - result = Curl_unencode_deflate_write(conn, &data->req, - (ssize_t)piece); - break; - - case GZIP: - /* update data->req.keep.str to point to the chunk data. */ - data->req.str = datap; - result = Curl_unencode_gzip_write(conn, &data->req, - (ssize_t)piece); - break; - - default: - failf(conn->data, - "Unrecognized content encoding type. " - "libcurl understands `identity', `deflate' and `gzip' " - "content encodings."); - return CHUNKE_BAD_ENCODING; + if(conn->data->set.http_ce_skip || !k->writer_stack) { + if(!k->ignorebody) + result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece); } -#endif + else + result = Curl_unencode_write(conn, k->writer_stack, datap, piece); if(result) return CHUNKE_WRITE_ERROR; *wrote += piece; - ch->datasize -= piece; /* decrease amount left to expect */ datap += piece; /* move read pointer forward */ length -= piece; /* decrease space left in this round */ diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index dff9d23..7f50405 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -167,6 +167,7 @@ static CURLcode connect_init(struct connectdata *conn, bool reinit) s->line_start = s->connect_buffer; s->ptr = s->line_start; s->cl = 0; + s->close_connection = FALSE; return CURLE_OK; } @@ -187,8 +188,7 @@ static CURLcode CONNECT(struct connectdata *conn, struct SingleRequest *k = &data->req; CURLcode result; curl_socket_t tunnelsocket = conn->sock[sockindex]; - bool closeConnection = FALSE; - time_t check; + timediff_t check; struct http_connect_state *s = conn->connect_state; #define SELECT_OK 0 @@ -529,7 +529,7 @@ static CURLcode CONNECT(struct connectdata *conn, } } else if(Curl_compareheader(s->line_start, "Connection:", "close")) - closeConnection = TRUE; + s->close_connection = TRUE; else if(checkprefix("Transfer-Encoding:", s->line_start)) { if(k->httpcode/100 == 2) { /* A client MUST ignore any Content-Length or Transfer-Encoding @@ -548,7 +548,7 @@ static CURLcode CONNECT(struct connectdata *conn, } else if(Curl_compareheader(s->line_start, "Proxy-Connection:", "close")) - closeConnection = TRUE; + s->close_connection = TRUE; else if(2 == sscanf(s->line_start, "HTTP/1.%d %d", &subversion, &k->httpcode)) { @@ -578,10 +578,10 @@ static CURLcode CONNECT(struct connectdata *conn, /* the connection has been marked for closure, most likely in the Curl_http_auth_act() function and thus we can kill it at once below */ - closeConnection = TRUE; + s->close_connection = TRUE; } - if(closeConnection && data->req.newurl) { + if(s->close_connection && data->req.newurl) { /* Connection closed by server. Don't use it anymore */ Curl_closesocket(conn, conn->sock[sockindex]); conn->sock[sockindex] = CURL_SOCKET_BAD; @@ -599,7 +599,7 @@ static CURLcode CONNECT(struct connectdata *conn, } while(data->req.newurl); if(data->info.httpproxycode/100 != 2) { - if(closeConnection && data->req.newurl) { + if(s->close_connection && data->req.newurl) { conn->bits.proxy_connect_closed = TRUE; infof(data, "Connect me again please\n"); connect_done(conn); diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 954d18f..cf278a2 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -275,15 +275,15 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, case IMAP_LIST: if((!imap->custom && !imap_matchresp(line, len, "LIST")) || (imap->custom && !imap_matchresp(line, len, imap->custom) && - (strcmp(imap->custom, "STORE") || + (!strcasecompare(imap->custom, "STORE") || !imap_matchresp(line, len, "FETCH")) && - strcmp(imap->custom, "SELECT") && - strcmp(imap->custom, "EXAMINE") && - strcmp(imap->custom, "SEARCH") && - strcmp(imap->custom, "EXPUNGE") && - strcmp(imap->custom, "LSUB") && - strcmp(imap->custom, "UID") && - strcmp(imap->custom, "NOOP"))) + !strcasecompare(imap->custom, "SELECT") && + !strcasecompare(imap->custom, "EXAMINE") && + !strcasecompare(imap->custom, "SEARCH") && + !strcasecompare(imap->custom, "EXPUNGE") && + !strcasecompare(imap->custom, "LSUB") && + !strcasecompare(imap->custom, "UID") && + !strcasecompare(imap->custom, "NOOP"))) return FALSE; break; @@ -344,23 +344,30 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, */ static void imap_get_message(char *buffer, char **outptr) { - size_t len = 0; + size_t len = strlen(buffer); char *message = NULL; - /* Find the start of the message */ - for(message = buffer + 2; *message == ' ' || *message == '\t'; message++) - ; - - /* Find the end of the message */ - for(len = strlen(message); len--;) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; + if(len > 2) { + /* Find the start of the message */ + len -= 2; + for(message = buffer + 2; *message == ' ' || *message == '\t'; + message++, len--) + ; + + /* Find the end of the message */ + for(; len--;) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; - /* Terminate the message */ - if(++len) { - message[len] = '\0'; + /* Terminate the message */ + if(++len) { + message[len] = '\0'; + } } + else + /* junk input => zero length output */ + message = &buffer[len]; *outptr = message; } @@ -1053,7 +1060,7 @@ static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode, else if(imapcode == IMAP_RESP_OK) { /* Check if the UIDVALIDITY has been specified and matches */ if(imap->uidvalidity && imapc->mailbox_uidvalidity && - strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) { + !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) { failf(conn->data, "Mailbox UIDVALIDITY has changed"); result = CURLE_REMOTE_FILE_NOT_FOUND; } @@ -1126,6 +1133,11 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode, /* The conversion from curl_off_t to size_t is always fine here */ chunk = (size_t)size; + if(!chunk) { + /* no size, we're done with the data */ + state(conn, IMAP_STOP); + return CURLE_OK; + } result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk); if(result) return result; @@ -1521,9 +1533,9 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected, /* Determine if the requested mailbox (with the same UIDVALIDITY if set) has already been selected on this connection */ if(imap->mailbox && imapc->mailbox && - !strcmp(imap->mailbox, imapc->mailbox) && + strcasecompare(imap->mailbox, imapc->mailbox) && (!imap->uidvalidity || !imapc->mailbox_uidvalidity || - !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity))) + strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity))) selected = TRUE; /* Start the first command in the DO phase */ diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index af25e92..70507df 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -2,7 +2,7 @@ * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2016 Daniel Stenberg + * Copyright (c) 2004 - 2017 Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -282,6 +282,7 @@ krb5_auth(void *app_data, struct connectdata *conn) break; } + _gssresp.value = NULL; /* make sure it is initialized */ p = data->state.buffer + 4; p = strstr(p, "ADAT="); if(p) { diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 040641c..89047bc 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -190,9 +190,11 @@ static int ldap_win_bind_auth(LDAP *server, const char *user, const char *passwd, unsigned long authflags) { ULONG method = 0; - SEC_WINNT_AUTH_IDENTITY cred = { 0, }; + SEC_WINNT_AUTH_IDENTITY cred; int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; + memset(&cred, 0, sizeof(cred)); + #if defined(USE_SPNEGO) if(authflags & CURLAUTH_NEGOTIATE) { method = LDAP_AUTH_NEGOTIATE; diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index 4bb0a51..f8769c2 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c @@ -106,7 +106,11 @@ Curl_llist_remove(struct curl_llist *list, struct curl_llist_element *e, e->next->prev = NULL; } else { - e->prev->next = e->next; + if(!e->prev) + list->head = e->next; + else + e->prev->next = e->next; + if(!e->next) list->tail = e->prev; else diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index 0eb249c..2b81c26 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -343,7 +343,12 @@ curl_socket_t curl_socket(int domain, int type, int protocol, "FD %s:%d socket() = %ld\n" : "FD %s:%d socket() = %zd\n"; - curl_socket_t sockfd = socket(domain, type, protocol); + curl_socket_t sockfd; + + if(countcheck("socket", line, source)) + return CURL_SOCKET_BAD; + + sockfd = socket(domain, type, protocol); if(source && (sockfd != CURL_SOCKET_BAD)) curl_memlog(fmt, source, line, sockfd); @@ -351,6 +356,35 @@ curl_socket_t curl_socket(int domain, int type, int protocol, return sockfd; } +SEND_TYPE_RETV curl_dosend(SEND_TYPE_ARG1 sockfd, + SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, + SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line, + const char *source) +{ + SEND_TYPE_RETV rc; + if(countcheck("send", line, source)) + return -1; + rc = send(sockfd, buf, len, flags); + if(source) + curl_memlog("SEND %s:%d send(%lu) = %ld\n", + source, line, (unsigned long)len, (long)rc); + return rc; +} + +RECV_TYPE_RETV curl_dorecv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, + RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line, + const char *source) +{ + RECV_TYPE_RETV rc; + if(countcheck("recv", line, source)) + return -1; + rc = recv(sockfd, buf, len, flags); + if(source) + curl_memlog("RECV %s:%d recv(%lu) = %ld\n", + source, line, (unsigned long)len, (long)rc); + return rc; +} + #ifdef HAVE_SOCKETPAIR int curl_socketpair(int domain, int type, int protocol, curl_socket_t socket_vector[2], diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index 835dab3..6fb8b68 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -66,6 +66,17 @@ CURL_EXTERN int curl_socketpair(int domain, int type, int protocol, int line, const char *source); #endif +/* send/receive sockets */ +CURL_EXTERN SEND_TYPE_RETV curl_dosend(SEND_TYPE_ARG1 sockfd, + SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, + SEND_TYPE_ARG3 len, + SEND_TYPE_ARG4 flags, int line, + const char *source); +CURL_EXTERN RECV_TYPE_RETV curl_dorecv(RECV_TYPE_ARG1 sockfd, + RECV_TYPE_ARG2 buf, RECV_TYPE_ARG3 len, + RECV_TYPE_ARG4 flags, int line, + const char *source); + /* FILE functions */ CURL_EXTERN FILE *curl_fopen(const char *file, const char *mode, int line, const char *source); @@ -84,6 +95,8 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source); #define calloc(nbelem,size) curl_docalloc(nbelem, size, __LINE__, __FILE__) #define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__) #define free(ptr) curl_dofree(ptr, __LINE__, __FILE__) +#define send(a,b,c,d) curl_dosend(a,b,c,d, __LINE__, __FILE__) +#define recv(a,b,c,d) curl_dorecv(a,b,c,d, __LINE__, __FILE__) #ifdef WIN32 # ifdef UNICODE diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 496f5e6..e0853a9 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,8 @@ #include "mime.h" #include "non-ascii.h" +#include "urldata.h" +#include "sendf.h" #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ !defined(CURL_DISABLE_IMAP) @@ -404,7 +406,7 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, while(st->bufbeg < st->bufend) { /* Line full ? */ - if(st->pos >= MAX_ENCODED_LINE_LENGTH - 4) { + if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) { /* Yes, we need 2 characters for CRLF. */ if(size < 2) break; @@ -419,7 +421,7 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, if(size < 4 || st->bufend - st->bufbeg < 3) break; - /* Encode three bytes a four characters. */ + /* Encode three bytes as four characters. */ i = st->buf[st->bufbeg++] & 0xFF; i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); @@ -618,14 +620,13 @@ static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, { curl_mimepart *part = (curl_mimepart *) instream; size_t sz = (size_t) part->datasize - part->state.offset; - (void) size; /* Always 1.*/ if(sz > nitems) sz = nitems; if(sz) - memcpy(buffer, (char *) part->data, sz); + memcpy(buffer, (char *) &part->data[part->state.offset], sz); part->state.offset += sz; return sz; @@ -718,8 +719,6 @@ static size_t readback_bytes(mime_state *state, { size_t sz; - sz = numbytes - state->offset; - if(numbytes > state->offset) { sz = numbytes - state->offset; bytes += state->offset; @@ -1069,13 +1068,6 @@ static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) return result; } -static void mime_subparts_free(void *ptr) -{ - curl_mime *mime = (curl_mime *) ptr; - curl_mime_free(mime); -} - - /* Release part content. */ static void cleanup_part_content(curl_mimepart *part) { @@ -1089,11 +1081,34 @@ static void cleanup_part_content(curl_mimepart *part) part->data = NULL; part->fp = NULL; part->datasize = (curl_off_t) 0; /* No size yet. */ - part->encoder = NULL; cleanup_encoder_state(&part->encstate); part->kind = MIMEKIND_NONE; } +static void mime_subparts_free(void *ptr) +{ + curl_mime *mime = (curl_mime *) ptr; + + if(mime && mime->parent) { + mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ + cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ + } + curl_mime_free(mime); +} + +/* Do not free subparts: unbind them. This is used for the top level only. */ +static void mime_subparts_unbind(void *ptr) +{ + curl_mime *mime = (curl_mime *) ptr; + + if(mime && mime->parent) { + mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ + cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ + mime->parent = NULL; + } +} + + void Curl_mime_cleanpart(curl_mimepart *part) { cleanup_part_content(part); @@ -1112,6 +1127,7 @@ void curl_mime_free(curl_mime *mime) curl_mimepart *part; if(mime) { + mime_subparts_unbind(mime); /* Be sure it's not referenced anymore. */ while(mime->firstpart) { part = mime->firstpart; mime->firstpart = part->nextpart; @@ -1124,6 +1140,78 @@ void curl_mime_free(curl_mime *mime) } } +CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) +{ + curl_mime *mime; + curl_mimepart *d; + const curl_mimepart *s; + CURLcode res = CURLE_OK; + + /* Duplicate content. */ + switch(src->kind) { + case MIMEKIND_NONE: + break; + case MIMEKIND_DATA: + res = curl_mime_data(dst, src->data, (size_t) src->datasize); + break; + case MIMEKIND_FILE: + res = curl_mime_filedata(dst, src->data); + /* Do not abort duplication if file is not readable. */ + if(res == CURLE_READ_ERROR) + res = CURLE_OK; + break; + case MIMEKIND_CALLBACK: + res = curl_mime_data_cb(dst, src->datasize, src->readfunc, + src->seekfunc, src->freefunc, src->arg); + break; + case MIMEKIND_MULTIPART: + /* No one knows about the cloned subparts, thus always attach ownership + to the part. */ + mime = curl_mime_init(dst->easy); + res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY; + + /* Duplicate subparts. */ + for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { + d = curl_mime_addpart(mime); + res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY; + } + break; + default: /* Invalid kind: should not occur. */ + res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ + break; + } + + /* Duplicate headers. */ + if(!res && src->userheaders) { + struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); + + if(!hdrs) + res = CURLE_OUT_OF_MEMORY; + else { + /* No one but this procedure knows about the new header list, + so always take ownership. */ + res = curl_mime_headers(dst, hdrs, TRUE); + if(res) + curl_slist_free_all(hdrs); + } + } + + /* Duplicate other fields. */ + dst->encoder = src->encoder; + if(!res) + res = curl_mime_type(dst, src->mimetype); + if(!res) + res = curl_mime_name(dst, src->name); + if(!res) + res = curl_mime_filename(dst, src->filename); + + /* If an error occurred, rollback. */ + if(res) + Curl_mime_cleanpart(dst); + + return res; +} + /* * Mime build functions. */ @@ -1356,7 +1444,8 @@ CURLcode curl_mime_headers(curl_mimepart *part, return CURLE_BAD_FUNCTION_ARGUMENT; if(part->flags & MIME_USERHEADERS_OWNER) { - curl_slist_free_all(part->userheaders); + if(part->userheaders != headers) /* Allow setting twice the same list. */ + curl_slist_free_all(part->userheaders); part->flags &= ~MIME_USERHEADERS_OWNER; } part->userheaders = headers; @@ -1389,9 +1478,11 @@ CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize, } /* Set mime part content from subparts. */ -CURLcode curl_mime_subparts(curl_mimepart *part, - curl_mime *subparts) +CURLcode Curl_mime_set_subparts(curl_mimepart *part, + curl_mime *subparts, int take_ownership) { + curl_mime *root; + if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1410,10 +1501,22 @@ CURLcode curl_mime_subparts(curl_mimepart *part, if(subparts->parent) return CURLE_BAD_FUNCTION_ARGUMENT; + /* Should not be the part's root. */ + root = part->parent; + if(root) { + while(root->parent && root->parent->parent) + root = root->parent->parent; + if(subparts == root) { + if(part->easy) + failf(part->easy, "Can't add itself as a subpart!"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + } + subparts->parent = part; part->readfunc = mime_subparts_read; part->seekfunc = mime_subparts_seek; - part->freefunc = mime_subparts_free; + part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind; part->arg = subparts; part->datasize = -1; part->kind = MIMEKIND_MULTIPART; @@ -1422,6 +1525,11 @@ CURLcode curl_mime_subparts(curl_mimepart *part, return CURLE_OK; } +CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) +{ + return Curl_mime_set_subparts(part, subparts, TRUE); +} + /* Readback from top mime. */ /* Argument is the dummy top part. */ @@ -1485,7 +1593,7 @@ curl_off_t Curl_mime_size(curl_mimepart *part) { curl_off_t size; - if(part->datasize < 0 && part->kind == MIMEKIND_MULTIPART) + if(part->kind == MIMEKIND_MULTIPART) part->datasize = multipart_size(part->arg); size = part->datasize; @@ -1581,7 +1689,7 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, { curl_mime *mime = NULL; const char *boundary = NULL; - char *s; + char *customct; const char *cte = NULL; CURLcode ret = CURLE_OK; @@ -1593,12 +1701,14 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, if(part->state.state == MIMESTATE_CURLHEADERS) mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL); - /* Build the content-type header. */ - s = search_header(part->userheaders, "Content-Type"); - if(s) - contenttype = s; - if(part->mimetype) - contenttype = part->mimetype; + /* Check if content type is specified. */ + customct = part->mimetype; + if(!customct) + customct = search_header(part->userheaders, "Content-Type"); + if(customct) + contenttype = customct; + + /* If content type is not specified, try to determine it. */ if(!contenttype) { switch(part->kind) { case MIMEKIND_MULTIPART: @@ -1622,7 +1732,8 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, if(mime) boundary = mime->boundary; } - else if(contenttype && strcasecompare(contenttype, "text/plain")) + else if(contenttype && !customct && + strcasecompare(contenttype, "text/plain")) if(strategy == MIMESTRATEGY_MAIL || !part->filename) contenttype = NULL; @@ -1816,6 +1927,22 @@ void Curl_mime_cleanpart(curl_mimepart *part) (void) part; } +CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) +{ + (void) dst; + (void) src; + return CURLE_OK; /* Nothing to duplicate: always succeed. */ +} + +CURLcode Curl_mime_set_subparts(curl_mimepart *part, + curl_mime *subparts, int take_ownership) +{ + (void) part; + (void) subparts; + (void) take_ownership; + return CURLE_NOT_BUILT_IN; +} + CURLcode Curl_mime_prepare_headers(curl_mimepart *part, const char *contenttype, const char *disposition, diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h index a144857..920a8a7 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -122,6 +122,9 @@ struct curl_mimepart_s { /* Prototypes. */ void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy); void Curl_mime_cleanpart(curl_mimepart *part); +CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src); +CURLcode Curl_mime_set_subparts(curl_mimepart *part, + curl_mime *subparts, int take_ownership); CURLcode Curl_mime_prepare_headers(curl_mimepart *part, const char *contenttype, const char *disposition, diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 70aa6bc..43823cc 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -59,7 +59,9 @@ #define CURL_SOCKET_HASH_TABLE_SIZE 911 #endif +#ifndef CURL_CONNECTION_HASH_SIZE #define CURL_CONNECTION_HASH_SIZE 97 +#endif #define CURL_MULTI_HANDLE 0x000bab1e @@ -324,14 +326,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_llist_init(&multi->msglist, multi_freeamsg); Curl_llist_init(&multi->pending, multi_freeamsg); - /* allocate a new easy handle to use when closing cached connections */ - multi->closure_handle = curl_easy_init(); - if(!multi->closure_handle) - goto error; - - multi->closure_handle->multi = multi; - multi->closure_handle->state.conn_cache = &multi->conn_cache; - multi->max_pipeline_length = 5; /* -1 means it not set by user, use the default value */ @@ -343,8 +337,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ Curl_hash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->hostcache); Curl_conncache_destroy(&multi->conn_cache); - Curl_close(multi->closure_handle); - multi->closure_handle = NULL; Curl_llist_destroy(&multi->msglist, NULL); Curl_llist_destroy(&multi->pending, NULL); @@ -405,8 +397,11 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->dns.hostcachetype = HCACHE_MULTI; } - /* Point to the multi's connection cache */ - data->state.conn_cache = &multi->conn_cache; + /* Point to the shared or multi handle connection cache */ + if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) + data->state.conn_cache = &data->share->conn_cache; + else + data->state.conn_cache = &multi->conn_cache; /* This adds the new entry at the 'end' of the doubly-linked circular list of Curl_easy structs to try and maintain a FIFO queue so @@ -460,8 +455,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, state somewhat we clone the timeouts from each added handle so that the closure handle always has the same timeouts as the most recently added easy handle. */ - multi->closure_handle->set.timeout = data->set.timeout; - multi->closure_handle->set.server_response_timeout = + data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; + data->state.conn_cache->closure_handle->set.server_response_timeout = data->set.server_response_timeout; update_timer(multi); @@ -484,38 +479,6 @@ static void debug_print_sock_hash(void *p) } #endif -/* Mark the connection as 'idle', or close it if the cache is full. - Returns TRUE if the connection is kept, or FALSE if it was closed. */ -static bool -ConnectionDone(struct Curl_easy *data, struct connectdata *conn) -{ - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: - data->multi->maxconnects; - struct connectdata *conn_candidate = NULL; - - /* Mark the current connection as 'unused' */ - conn->inuse = FALSE; - - if(maxconnects > 0 && - data->state.conn_cache->num_connections > maxconnects) { - infof(data, "Connection cache is full, closing the oldest one.\n"); - - conn_candidate = Curl_oldest_idle_connection(data); - - if(conn_candidate) { - /* Set the connection's owner correctly */ - conn_candidate->data = data; - - /* the winner gets the honour of being disconnected */ - (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); - } - } - - return (conn_candidate == conn) ? FALSE : TRUE; -} - static CURLcode multi_done(struct connectdata **connp, CURLcode status, /* an error if this is called after an error was detected */ @@ -589,6 +552,7 @@ static CURLcode multi_done(struct connectdata **connp, Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ conn->dns_entry = NULL; } + Curl_hostcache_prune(data); /* if the transfer was completed in a paused state there can be buffered data left to free */ @@ -617,7 +581,8 @@ static CURLcode multi_done(struct connectdata **connp, && !(conn->ntlm.state == NTLMSTATE_TYPE2 || conn->proxyntlm.state == NTLMSTATE_TYPE2) #endif - ) || conn->bits.close || premature) { + ) || conn->bits.close + || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */ /* If we had an error already, make sure we return that one. But @@ -626,17 +591,21 @@ static CURLcode multi_done(struct connectdata **connp, result = res2; } else { + char buffer[256]; + /* create string before returning the connection */ + snprintf(buffer, sizeof(buffer), + "Connection #%ld to host %s left intact", + conn->connection_id, + conn->bits.socksproxy ? conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname); + /* the connection is no longer in use */ - if(ConnectionDone(data, conn)) { + if(Curl_conncache_return_conn(conn)) { /* remember the most recently used connection */ data->state.lastconnect = conn; - - infof(data, "Connection #%ld to host %s left intact\n", - conn->connection_id, - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname); + infof(data, "%s\n", buffer); } else data->state.lastconnect = NULL; @@ -705,12 +674,6 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, curl_easy_cleanup is called. */ Curl_expire_clear(data); - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* stop using the multi handle's DNS cache */ - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - if(data->easy_conn) { /* we must call multi_done() here (if we still own the connection) so that @@ -729,6 +692,13 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, Curl_getoff_all_pipelines(data, data->easy_conn); } + if(data->dns.hostcachetype == HCACHE_MULTI) { + /* stop using the multi handle's DNS cache, *after* the possible + multi_done() call above */ + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + Curl_wildcard_dtor(&data->wildcard); /* destroy the timeout list that is held in the easy handle, do this *after* @@ -1315,7 +1285,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, struct SingleRequest *k; time_t timeout_ms; time_t recv_timeout_ms; - time_t send_timeout_ms; + timediff_t send_timeout_ms; int control; if(!GOOD_EASY_HANDLE(data)) @@ -1361,16 +1331,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT && - data->mstate < CURLM_STATE_COMPLETED) + data->mstate < CURLM_STATE_COMPLETED) { /* Make sure we set the connection's current owner */ data->easy_conn->data = data; + } if(data->easy_conn && (data->mstate >= CURLM_STATE_CONNECT) && (data->mstate < CURLM_STATE_COMPLETED)) { /* we need to wait for the connect state as only then is the start time stored, but we must not check already completed handles */ - timeout_ms = Curl_timeleft(data, &now, (data->mstate <= CURLM_STATE_WAITDO)? TRUE:FALSE); @@ -1379,23 +1349,23 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Handle timed out */ if(data->mstate == CURLM_STATE_WAITRESOLVE) failf(data, "Resolving timed out after %ld milliseconds", - Curl_tvdiff(now, data->progress.t_startsingle)); + Curl_timediff(now, data->progress.t_startsingle)); else if(data->mstate == CURLM_STATE_WAITCONNECT) failf(data, "Connection timed out after %ld milliseconds", - Curl_tvdiff(now, data->progress.t_startsingle)); + Curl_timediff(now, data->progress.t_startsingle)); else { k = &data->req; if(k->size != -1) { failf(data, "Operation timed out after %ld milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_tvdiff(now, data->progress.t_startsingle), + Curl_timediff(now, data->progress.t_startsingle), k->bytecount, k->size); } else { failf(data, "Operation timed out after %ld milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_tvdiff(now, data->progress.t_startsingle), + Curl_timediff(now, data->progress.t_startsingle), k->bytecount); } } @@ -1660,7 +1630,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result) { if(!dophase_done) { /* some steps needed for wildcard matching */ - if(data->set.wildcardmatch) { + if(data->state.wildcardmatch) { struct WildcardData *wc = &data->wildcard; if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { /* skip some states if it is important */ @@ -1812,7 +1782,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, (data->easy_conn->writesockfd != CURL_SOCKET_BAD)) multistate(data, CURLM_STATE_WAITPERFORM); else + { + if(data->state.wildcardmatch && + ((data->easy_conn->handler->flags & PROTOPT_WILDCARD) == 0)) { + data->wildcard.state = CURLWC_DONE; + } multistate(data, CURLM_STATE_DONE); + } rc = CURLM_CALL_MULTI_PERFORM; break; @@ -2029,7 +2005,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->easy_conn = NULL; } - if(data->set.wildcardmatch) { + if(data->state.wildcardmatch) { if(data->wildcard.state != CURLWC_DONE) { /* if a wildcard is set and we are not ending -> lets start again with CURLM_STATE_INIT */ @@ -2146,7 +2122,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) struct Curl_easy *data; CURLMcode returncode = CURLM_OK; struct Curl_tree *t; - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -2192,61 +2168,21 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) return returncode; } -static void close_all_connections(struct Curl_multi *multi) -{ - struct connectdata *conn; - - conn = Curl_conncache_find_first_connection(&multi->conn_cache); - while(conn) { - SIGPIPE_VARIABLE(pipe_st); - conn->data = multi->closure_handle; - - sigpipe_ignore(conn->data, &pipe_st); - conn->data->easy_conn = NULL; /* clear the easy handle's connection - pointer */ - /* This will remove the connection from the cache */ - connclose(conn, "kill all"); - (void)Curl_disconnect(conn, FALSE); - sigpipe_restore(&pipe_st); - - conn = Curl_conncache_find_first_connection(&multi->conn_cache); - } -} - CURLMcode curl_multi_cleanup(struct Curl_multi *multi) { struct Curl_easy *data; struct Curl_easy *nextdata; if(GOOD_MULTI_HANDLE(multi)) { - bool restore_pipe = FALSE; - SIGPIPE_VARIABLE(pipe_st); - multi->type = 0; /* not good anymore */ - /* Close all the connections in the connection cache */ - close_all_connections(multi); - - if(multi->closure_handle) { - sigpipe_ignore(multi->closure_handle, &pipe_st); - restore_pipe = TRUE; - - multi->closure_handle->dns.hostcache = &multi->hostcache; - Curl_hostcache_clean(multi->closure_handle, - multi->closure_handle->dns.hostcache); - - Curl_close(multi->closure_handle); - } - - Curl_hash_destroy(&multi->sockhash); - Curl_conncache_destroy(&multi->conn_cache); - Curl_llist_destroy(&multi->msglist, NULL); - Curl_llist_destroy(&multi->pending, NULL); - - /* remove all easy handles */ + /* Firsrt remove all remaining easy handles */ data = multi->easyp; while(data) { nextdata = data->next; + if(!data->state.done && data->easy_conn) + /* if DONE was never called for this handle */ + (void)multi_done(&data->easy_conn, CURLE_OK, TRUE); if(data->dns.hostcachetype == HCACHE_MULTI) { /* clear out the usage of the shared DNS cache */ Curl_hostcache_clean(data, data->dns.hostcache); @@ -2261,6 +2197,14 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) data = nextdata; } + /* Close all the connections in the connection cache */ + Curl_conncache_close_all_connections(&multi->conn_cache); + + Curl_hash_destroy(&multi->sockhash); + Curl_conncache_destroy(&multi->conn_cache); + Curl_llist_destroy(&multi->msglist, NULL); + Curl_llist_destroy(&multi->pending, NULL); + Curl_hash_destroy(&multi->hostcache); /* Free the blacklists by setting them to NULL */ @@ -2268,8 +2212,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl); free(multi); - if(restore_pipe) - sigpipe_restore(&pipe_st); return CURLM_OK; } @@ -2510,9 +2452,9 @@ static CURLMcode add_next_timeout(struct curltime now, timeout in *tv */ for(e = list->head; e;) { struct curl_llist_element *n = e->next; - time_t diff; + timediff_t diff; node = (struct time_node *)e->ptr; - diff = curlx_tvdiff(node->time, now); + diff = Curl_timediff(node->time, now); if(diff <= 0) /* remove outdated entry */ Curl_llist_remove(list, e, NULL); @@ -2549,7 +2491,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, CURLMcode result = CURLM_OK; struct Curl_easy *data = NULL; struct Curl_tree *t; - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); if(checkall) { /* *perform() deals with running_handles on its own */ @@ -2625,8 +2567,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, data = NULL; /* set data to NULL again to avoid calling multi_runsingle() in case there's no need to */ - now = Curl_tvnow(); /* get a newer time since the multi_runsingle() loop - may have taken some time */ + now = Curl_now(); /* get a newer time since the multi_runsingle() loop + may have taken some time */ } } else { @@ -2779,15 +2721,15 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, if(multi->timetree) { /* we have a tree of expire times */ - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); /* splay the lowest to the bottom */ multi->timetree = Curl_splay(tv_zero, multi->timetree); if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { /* some time left before expiration */ - *timeout_ms = (long)curlx_tvdiff(multi->timetree->key, now); - if(!*timeout_ms) + timediff_t diff = Curl_timediff(multi->timetree->key, now); + if(diff <= 0) /* * Since we only provide millisecond resolution on the returned value * and the diff might be less than one millisecond here, we don't @@ -2796,6 +2738,10 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, * millisecond! instead we return 1 until the time is ripe. */ *timeout_ms = 1; + else + /* this should be safe even on 64 bit archs, as we don't use that + overly long timeouts */ + *timeout_ms = (long)diff; } else /* 0 means immediately */ @@ -2902,7 +2848,7 @@ multi_addtimeout(struct Curl_easy *data, /* find the correct spot in the list */ for(e = timeoutlist->head; e; e = e->next) { struct time_node *check = (struct time_node *)e->ptr; - time_t diff = curlx_tvdiff(check->time, node->time); + timediff_t diff = Curl_timediff(check->time, node->time); if(diff > 0) break; prev = e; @@ -2941,7 +2887,7 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id) DEBUGASSERT(id < EXPIRE_LAST); - set = Curl_tvnow(); + set = Curl_now(); set.tv_sec += milli/1000; set.tv_usec += (unsigned int)(milli%1000)*1000; @@ -2961,7 +2907,7 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id) /* This means that the struct is added as a node in the splay tree. Compare if the new time is earlier, and only remove-old/add-new if it is. */ - time_t diff = curlx_tvdiff(set, *nowp); + timediff_t diff = Curl_timediff(set, *nowp); if(diff > 0) { /* The current splay tree entry is sooner than this new expiry time. diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 4057539..de9a7cf 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -114,10 +114,6 @@ struct Curl_multi { /* Shared connection cache (bundles)*/ struct conncache conn_cache; - /* This handle will be used for closing the cached connections in - curl_multi_cleanup() */ - struct Curl_easy *closure_handle; - long maxconnects; /* if >0, a fixed limit of the maximum number of entries we're allowed to grow the connection cache to */ diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index f294403..f2ffdfe 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -51,6 +51,25 @@ #include "curl_memory.h" #include "memdebug.h" +/* + * Uncommenting this will enable the built-in debug logging of the openldap + * library. The debug log level can be set using the CURL_OPENLDAP_TRACE + * environment variable. The debug output is written to stderr. + * + * The library supports the following debug flags: + * LDAP_DEBUG_NONE 0x0000 + * LDAP_DEBUG_TRACE 0x0001 + * LDAP_DEBUG_CONSTRUCT 0x0002 + * LDAP_DEBUG_DESTROY 0x0004 + * LDAP_DEBUG_PARAMETER 0x0008 + * LDAP_DEBUG_ANY 0xffff + * + * For example, use CURL_OPENLDAP_TRACE=0 for no debug, + * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only, + * CURL_OPENLDAP_TRACE=65535 for all debug message levels. + */ +/* #define CURL_OPENLDAP_DEBUG */ + #ifndef _LDAP_PVT_H extern int ldap_pvt_url_scheme2proto(const char *); extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, @@ -204,6 +223,15 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done) snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d", conn->host.name, conn->remote_port); +#ifdef CURL_OPENLDAP_DEBUG + static int do_trace = 0; + const char *env = getenv("CURL_OPENLDAP_TRACE"); + do_trace = (env && strtol(env, NULL, 10) > 0); + if(do_trace) { + ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); + } +#endif + rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); if(rc) { failf(data, "LDAP local: Cannot connect to %s, %s", @@ -677,7 +705,7 @@ ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) ber_slen_t ret; CURLcode err = CURLE_RECV_ERROR; - ret = li->recv(conn, FIRSTSOCKET, buf, len, &err); + ret = (li->recv)(conn, FIRSTSOCKET, buf, len, &err); if(ret < 0 && err == CURLE_AGAIN) { SET_SOCKERRNO(EWOULDBLOCK); } @@ -692,7 +720,7 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) ber_slen_t ret; CURLcode err = CURLE_SEND_ERROR; - ret = li->send(conn, FIRSTSOCKET, buf, len, &err); + ret = (li->send)(conn, FIRSTSOCKET, buf, len, &err); if(ret < 0 && err == CURLE_AGAIN) { SET_SOCKERRNO(EWOULDBLOCK); } diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index b82605b..0fabbd2 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c @@ -75,9 +75,7 @@ #include "curl_setup.h" -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #include <curl/curl.h> #include "strcase.h" diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index b8f2140..438856a 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c @@ -61,12 +61,12 @@ time_t Curl_pp_state_timeout(struct pingpong *pp) /* Without a requested timeout, we only wait 'response_time' seconds for the full response to arrive before we bail out */ timeout_ms = response_time - - Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */ + Curl_timediff(Curl_now(), pp->response); /* spent time */ if(data->set.timeout) { /* if timeout is requested, find out how much remaining time we have */ timeout2_ms = data->set.timeout - /* timeout time */ - Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ + Curl_timediff(Curl_now(), conn->now); /* spent time */ /* pick the lowest number */ timeout_ms = CURLMIN(timeout_ms, timeout2_ms); @@ -120,7 +120,7 @@ CURLcode Curl_pp_statemach(struct pingpong *pp, bool block) if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, Curl_tvnow()); + result = Curl_speedcheck(data, Curl_now()); if(result) return result; @@ -143,7 +143,7 @@ void Curl_pp_init(struct pingpong *pp) pp->nread_resp = 0; pp->linestart_resp = conn->data->state.buffer; pp->pending_resp = TRUE; - pp->response = Curl_tvnow(); /* start response time-out now! */ + pp->response = Curl_now(); /* start response time-out now! */ } @@ -168,16 +168,22 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, char *s; CURLcode result; struct connectdata *conn = pp->conn; - struct Curl_easy *data = conn->data; + struct Curl_easy *data; #ifdef HAVE_GSSAPI - enum protection_level data_sec = conn->data_prot; + enum protection_level data_sec; #endif DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); DEBUGASSERT(pp->sendthis == NULL); + if(!conn) + /* can't send without a connection! */ + return CURLE_SEND_ERROR; + + data = conn->data; + fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */ if(!fmt_crlf) return CURLE_OUT_OF_MEMORY; @@ -205,6 +211,7 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len, &bytes_written); #ifdef HAVE_GSSAPI + data_sec = conn->data_prot; DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif @@ -228,7 +235,7 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp, free(s); pp->sendthis = NULL; pp->sendleft = pp->sendsize = 0; - pp->response = Curl_tvnow(); + pp->response = Curl_now(); } return CURLE_OK; @@ -492,7 +499,7 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp) free(pp->sendthis); pp->sendthis = NULL; pp->sendleft = pp->sendsize = 0; - pp->response = Curl_tvnow(); + pp->response = Curl_now(); } return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index a2c8ff5..5ac8df8 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h @@ -58,8 +58,8 @@ struct pingpong { server */ size_t sendleft; /* number of bytes left to send from the sendthis buffer */ size_t sendsize; /* total size of the sendthis buffer */ - struct curltime response; /* set to Curl_tvnow() when a command has been sent - off, used to time-out response reading */ + struct curltime response; /* set to Curl_now() when a command has been sent + off, used to time-out response reading */ long response_time; /* When no timeout is given, this is the amount of milliseconds we await for a server response. */ diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index 5792a4a..78f6afe 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -243,23 +243,30 @@ static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, */ static void pop3_get_message(char *buffer, char **outptr) { - size_t len = 0; + size_t len = strlen(buffer); char *message = NULL; - /* Find the start of the message */ - for(message = buffer + 2; *message == ' ' || *message == '\t'; message++) - ; - - /* Find the end of the message */ - for(len = strlen(message); len--;) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; - - /* Terminate the message */ - if(++len) { - message[len] = '\0'; + if(len > 2) { + /* Find the start of the message */ + len -= 2; + for(message = buffer + 2; *message == ' ' || *message == '\t'; + message++, len--) + ; + + /* Find the end of the message */ + for(; len--;) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; + + /* Terminate the message */ + if(++len) { + message[len] = '\0'; + } } + else + /* junk input => zero length output */ + message = &buffer[len]; *outptr = message; } diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index 00609d9..cc5e8be 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -161,7 +161,7 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) */ void Curl_pgrsTime(struct Curl_easy *data, timerid timer) { - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); time_t *delta = NULL; switch(timer) { @@ -212,13 +212,13 @@ void Curl_pgrsTime(struct Curl_easy *data, timerid timer) /* this is the normal end-of-transfer thing */ break; case TIMER_REDIRECT: - data->progress.t_redirect = Curl_tvdiff_us(now, data->progress.start); + data->progress.t_redirect = Curl_timediff_us(now, data->progress.start); break; } if(delta) { - time_t us = Curl_tvdiff_us(now, data->progress.t_startsingle); - if(!us) - us++; /* make sure at least one microsecond passed */ + timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle); + if(us < 1) + us = 1; /* make sure at least one microsecond passed */ *delta += us; } } @@ -226,7 +226,7 @@ void Curl_pgrsTime(struct Curl_easy *data, timerid timer) void Curl_pgrsStartNow(struct Curl_easy *data) { data->progress.speeder_c = 0; /* reset the progress meter display */ - data->progress.start = Curl_tvnow(); + data->progress.start = Curl_now(); data->progress.is_t_startransfer_set = false; data->progress.ul_limit_start.tv_sec = 0; data->progress.ul_limit_start.tv_usec = 0; @@ -274,7 +274,7 @@ long Curl_pgrsLimitWaitTime(curl_off_t cursize, return -1; minimum = (time_t) (CURL_OFF_T_C(1000) * size / limit); - actual = Curl_tvdiff(now, start); + actual = Curl_timediff(now, start); if(actual < minimum) /* this is a conversion on some systems (64bit time_t => 32bit long) */ @@ -285,7 +285,7 @@ long Curl_pgrsLimitWaitTime(curl_off_t cursize, void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) { - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); data->progress.downloaded = size; @@ -303,7 +303,7 @@ void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size) { - struct curltime now = Curl_tvnow(); + struct curltime now = Curl_now(); data->progress.uploaded = size; @@ -358,6 +358,7 @@ int Curl_pgrsUpdate(struct connectdata *conn) curl_off_t total_transfer; curl_off_t total_expected_transfer; curl_off_t timespent; + curl_off_t timespent_ms; /* milliseconds */ struct Curl_easy *data = conn->data; int nowindex = data->progress.speeder_c% CURR_TIME; int checkindex; @@ -369,22 +370,27 @@ int Curl_pgrsUpdate(struct connectdata *conn) curl_off_t dlestimate = 0; curl_off_t total_estimate; bool shownow = FALSE; + curl_off_t dl = data->progress.downloaded; + curl_off_t ul = data->progress.uploaded; - now = Curl_tvnow(); /* what time is it */ + now = Curl_now(); /* what time is it */ /* The time spent so far (from the start) */ - data->progress.timespent = Curl_tvdiff_us(now, data->progress.start); + data->progress.timespent = Curl_timediff_us(now, data->progress.start); timespent = (curl_off_t)data->progress.timespent/1000000; /* seconds */ + timespent_ms = (curl_off_t)data->progress.timespent/1000; /* ms */ /* The average download speed this far */ - data->progress.dlspeed = (curl_off_t) - (data->progress.downloaded/ - (timespent>0?timespent:1)); + if(dl < CURL_OFF_T_MAX/1000) + data->progress.dlspeed = (dl * 1000 / (timespent_ms>0?timespent_ms:1)); + else + data->progress.dlspeed = (dl / (timespent>0?timespent:1)); /* The average upload speed this far */ - data->progress.ulspeed = (curl_off_t) - (data->progress.uploaded/ - (timespent>0?timespent:1)); + if(ul < CURL_OFF_T_MAX/1000) + data->progress.ulspeed = (ul * 1000 / (timespent_ms>0?timespent_ms:1)); + else + data->progress.ulspeed = (ul / (timespent>0?timespent:1)); /* Calculations done at most once a second, unless end is reached */ if(data->progress.lastshow != now.tv_sec) { @@ -413,7 +419,7 @@ int Curl_pgrsUpdate(struct connectdata *conn) /* first of all, we don't do this if there's no counted seconds yet */ if(countindex) { - time_t span_ms; + timediff_t span_ms; /* Get the index position to compare with the 'nowindex' position. Get the oldest entry possible. While we have less than CURR_TIME @@ -422,8 +428,8 @@ int Curl_pgrsUpdate(struct connectdata *conn) data->progress.speeder_c%CURR_TIME:0; /* Figure out the exact time for the time span */ - span_ms = Curl_tvdiff(now, - data->progress.speeder_time[checkindex]); + span_ms = Curl_timediff(now, + data->progress.speeder_time[checkindex]); if(0 == span_ms) span_ms = 1; /* at least one millisecond MUST have passed */ diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 2713a0a..0769ed1 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -86,7 +86,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) #endif if(!seeded) { - struct curltime now = curlx_tvnow(); + struct curltime now = Curl_now(); infof(data, "WARNING: Using weak random seed\n"); randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; randseed = randseed * 1103515245 + 12345; @@ -157,6 +157,12 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, unsigned char *bufp = buffer; DEBUGASSERT(num > 1); +#ifdef __clang_analyzer__ + /* This silences a scan-build warning about accesssing this buffer with + uninitialized memory. */ + memset(buffer, 0, sizeof(buffer)); +#endif + if((num/2 >= sizeof(buffer)) || !(num&1)) /* make sure it fits in the local buffer and that it is an odd number! */ return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/Utilities/cmcurl/lib/security.c b/Utilities/cmcurl/lib/security.c index a2e4a35..d171985 100644 --- a/Utilities/cmcurl/lib/security.c +++ b/Utilities/cmcurl/lib/security.c @@ -50,9 +50,7 @@ #include <netdb.h> #endif -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #include "urldata.h" #include "curl_base64.h" diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index b8e1868..0406dd2 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -51,7 +51,7 @@ #include "warnless.h" /* Convenience local macros */ -#define ELAPSED_MS() (int)curlx_tvdiff(curlx_tvnow(), initial_tv) +#define ELAPSED_MS() (int)Curl_timediff(Curl_now(), initial_tv) int Curl_ack_eintr = 0; #define ERROR_NOT_EINTR(error) (Curl_ack_eintr || error != EINTR) @@ -96,7 +96,7 @@ int Curl_wait_ms(int timeout_ms) Sleep(timeout_ms); #else pending_ms = timeout_ms; - initial_tv = curlx_tvnow(); + initial_tv = Curl_now(); do { #if defined(HAVE_POLL_FINE) r = poll(NULL, 0, pending_ms); @@ -177,14 +177,14 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ return r; } - /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed + /* Avoid initial timestamp, avoid Curl_now() call, when elapsed time in this function does not need to be measured. This happens when function is called with a zero timeout or a negative timeout value indicating a blocking call should be performed. */ if(timeout_ms > 0) { pending_ms = (int)timeout_ms; - initial_tv = curlx_tvnow(); + initial_tv = Curl_now(); } #ifdef HAVE_POLL_FINE @@ -418,14 +418,14 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) return r; } - /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed + /* Avoid initial timestamp, avoid Curl_now() call, when elapsed time in this function does not need to be measured. This happens when function is called with a zero timeout or a negative timeout value indicating a blocking call should be performed. */ if(timeout_ms > 0) { pending_ms = timeout_ms; - initial_tv = curlx_tvnow(); + initial_tv = Curl_now(); } #ifdef HAVE_POLL_FINE diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 7564cb3..027f97c 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -22,6 +22,14 @@ #include "curl_setup.h" +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_LINUX_TCP_H +#include <linux/tcp.h> +#endif + #include <curl/curl.h> #include "urldata.h" @@ -241,25 +249,25 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...) void Curl_failf(struct Curl_easy *data, const char *fmt, ...) { - va_list ap; - size_t len; - char error[CURL_ERROR_SIZE + 2]; - va_start(ap, fmt); - - vsnprintf(error, CURL_ERROR_SIZE, fmt, ap); - len = strlen(error); + if(data->set.verbose || data->set.errorbuffer) { + va_list ap; + size_t len; + char error[CURL_ERROR_SIZE + 2]; + va_start(ap, fmt); + vsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + len = strlen(error); - if(data->set.errorbuffer && !data->state.errorbuf) { - strcpy(data->set.errorbuffer, error); - data->state.errorbuf = TRUE; /* wrote error string */ - } - if(data->set.verbose) { - error[len] = '\n'; - error[++len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, error, len, NULL); + if(data->set.errorbuffer && !data->state.errorbuf) { + strcpy(data->set.errorbuffer, error); + data->state.errorbuf = TRUE; /* wrote error string */ + } + if(data->set.verbose) { + error[len] = '\n'; + error[++len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, error, len, NULL); + } + va_end(ap); } - - va_end(ap); } /* Curl_sendf() sends formatted data to the server */ @@ -360,7 +368,7 @@ ssize_t Curl_send_plain(struct connectdata *conn, int num, available. */ pre_receive_plain(conn, num); -#ifdef MSG_FASTOPEN /* Linux */ +#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(conn->bits.tcp_fastopen) { bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen); diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c new file mode 100644 index 0000000..a5ef75c --- /dev/null +++ b/Utilities/cmcurl/lib/setopt.c @@ -0,0 +1,2556 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include <limits.h> + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_LINUX_TCP_H +#include <linux/tcp.h> +#endif + +#include "urldata.h" +#include "url.h" +#include "progress.h" +#include "content_encoding.h" +#include "strcase.h" +#include "share.h" +#include "vtls/vtls.h" +#include "warnless.h" +#include "sendf.h" +#include "http2.h" +#include "setopt.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +CURLcode Curl_setstropt(char **charp, const char *s) +{ + /* Release the previous storage at `charp' and replace by a dynamic storage + copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ + + Curl_safefree(*charp); + + if(s) { + char *str = strdup(s); + + if(!str) + return CURLE_OUT_OF_MEMORY; + + *charp = str; + } + + return CURLE_OK; +} + +static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) +{ + CURLcode result = CURLE_OK; + char *user = NULL; + char *passwd = NULL; + + /* Parse the login details if specified. It not then we treat NULL as a hint + to clear the existing data */ + if(option) { + result = Curl_parse_login_details(option, strlen(option), + (userp ? &user : NULL), + (passwdp ? &passwd : NULL), + NULL); + } + + if(!result) { + /* Store the username part of option if required */ + if(userp) { + if(!user && option && option[0] == ':') { + /* Allocate an empty string instead of returning NULL as user name */ + user = strdup(""); + if(!user) + result = CURLE_OUT_OF_MEMORY; + } + + Curl_safefree(*userp); + *userp = user; + } + + /* Store the password part of option if required */ + if(passwdp) { + Curl_safefree(*passwdp); + *passwdp = passwd; + } + } + + return result; +} + +#define C_SSLVERSION_VALUE(x) (x & 0xffff) +#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000) + +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, + va_list param) +{ + char *argptr; + CURLcode result = CURLE_OK; + long arg; + curl_off_t bigsize; + + switch(option) { + case CURLOPT_DNS_CACHE_TIMEOUT: + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.dns_cache_timeout = arg; + break; + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* remember we want this enabled */ + arg = va_arg(param, long); + data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE; + break; + case CURLOPT_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSL_CIPHER_LIST: + /* set a list of cipher we want to use in the SSL connection for proxy */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], + va_arg(param, char *)); + break; + + case CURLOPT_RANDOM_FILE: + /* + * This is the path name to a file that contains random data to seed + * the random SSL stuff with. The file is only used for reading. + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_RANDOM_FILE], + va_arg(param, char *)); + break; + case CURLOPT_EGDSOCKET: + /* + * The Entropy Gathering Daemon socket pathname + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_EGDSOCKET], + va_arg(param, char *)); + break; + case CURLOPT_MAXCONNECTS: + /* + * Set the absolute number of maximum simultaneous alive connection that + * libcurl is allowed to have. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxconnects = arg; + break; + case CURLOPT_FORBID_REUSE: + /* + * When this transfer is done, it must not be left to be reused by a + * subsequent transfer but shall be closed immediately. + */ + data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_FRESH_CONNECT: + /* + * This transfer shall not use a previously cached connection but + * should be made with a fresh new connect! + */ + data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_VERBOSE: + /* + * Verbose means infof() calls that give a lot of information about + * the connection and transfer procedures as well as internal choices. + */ + data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_HEADER: + /* + * Set to include the header in the general data output stream. + */ + data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_NOPROGRESS: + /* + * Shut off the internal supported progress meter + */ + data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE; + if(data->set.hide_progress) + data->progress.flags |= PGRS_HIDE; + else + data->progress.flags &= ~PGRS_HIDE; + break; + case CURLOPT_NOBODY: + /* + * Do not include the body part in the output data stream. + */ + data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_FAILONERROR: + /* + * Don't output the >=400 error code HTML-page, but instead only + * return error. + */ + data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_KEEP_SENDING_ON_ERROR: + data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ? + TRUE : FALSE; + break; + case CURLOPT_UPLOAD: + case CURLOPT_PUT: + /* + * We want to sent data to the remote host. If this is HTTP, that equals + * using the PUT request. + */ + data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; + if(data->set.upload) { + /* If this is HTTP, PUT is what's needed to "upload" */ + data->set.httpreq = HTTPREQ_PUT; + data->set.opt_no_body = FALSE; /* this is implied */ + } + else + /* In HTTP, the opposite of upload is GET (unless NOBODY is true as + then this can be changed to HEAD later on) */ + data->set.httpreq = HTTPREQ_GET; + break; + case CURLOPT_REQUEST_TARGET: + result = Curl_setstropt(&data->set.str[STRING_TARGET], + va_arg(param, char *)); + break; + case CURLOPT_FILETIME: + /* + * Try to get the file time of the remote document. The time will + * later (possibly) become available using curl_easy_getinfo(). + */ + data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_FTP_CREATE_MISSING_DIRS: + /* + * An FTP option that modifies an upload to create missing directories on + * the server. + */ + switch(va_arg(param, long)) { + case 0: + data->set.ftp_create_missing_dirs = 0; + break; + case 1: + data->set.ftp_create_missing_dirs = 1; + break; + case 2: + data->set.ftp_create_missing_dirs = 2; + break; + default: + /* reserve other values for future use */ + result = CURLE_UNKNOWN_OPTION; + break; + } + break; + case CURLOPT_SERVER_RESPONSE_TIMEOUT: + /* + * Option that specifies how quickly an server response must be obtained + * before it is considered failure. For pingpong protocols. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.server_response_timeout = arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + case CURLOPT_TFTP_NO_OPTIONS: + /* + * Option that prevents libcurl from sending TFTP option requests to the + * server. + */ + data->set.tftp_no_options = va_arg(param, long) != 0; + break; + case CURLOPT_TFTP_BLKSIZE: + /* + * TFTP option that specifies the block size to use for data transmission. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.tftp_blksize = arg; + break; + case CURLOPT_DIRLISTONLY: + /* + * An option that changes the command to one that asks for a list + * only, no file info details. + */ + data->set.ftp_list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_APPEND: + /* + * We want to upload and append to an existing file. + */ + data->set.ftp_append = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_FTP_FILEMETHOD: + /* + * How do access files over FTP. + */ + arg = va_arg(param, long); + if((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftp_filemethod = (curl_ftpfile)arg; + break; + case CURLOPT_NETRC: + /* + * Parse the $HOME/.netrc file + */ + arg = va_arg(param, long); + if((arg < CURL_NETRC_IGNORED) || (arg > CURL_NETRC_REQUIRED)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_netrc = (enum CURL_NETRC_OPTION)arg; + break; + case CURLOPT_NETRC_FILE: + /* + * Use this file instead of the $HOME/.netrc file + */ + result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE], + va_arg(param, char *)); + break; + case CURLOPT_TRANSFERTEXT: + /* + * This option was previously named 'FTPASCII'. Renamed to work with + * more protocols than merely FTP. + * + * Transfer using ASCII (instead of BINARY). + */ + data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_TIMECONDITION: + /* + * Set HTTP time condition. This must be one of the defines in the + * curl/curl.h header file. + */ + arg = va_arg(param, long); + if((arg < CURL_TIMECOND_NONE) || (arg > CURL_TIMECOND_LASTMOD)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.timecondition = (curl_TimeCond)arg; + break; + case CURLOPT_TIMEVALUE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = (time_t)va_arg(param, long); + break; + + case CURLOPT_SSLVERSION: + case CURLOPT_PROXY_SSLVERSION: + /* + * Set explicit SSL version to try to connect with, as some SSL + * implementations are lame. + */ +#ifdef USE_SSL + { + long version, version_max; + struct ssl_primary_config *primary = (option == CURLOPT_SSLVERSION ? + &data->set.ssl.primary : + &data->set.proxy_ssl.primary); + + arg = va_arg(param, long); + + version = C_SSLVERSION_VALUE(arg); + version_max = C_SSLVERSION_MAX_VALUE(arg); + + if(version < CURL_SSLVERSION_DEFAULT || + version >= CURL_SSLVERSION_LAST || + version_max < CURL_SSLVERSION_MAX_NONE || + version_max >= CURL_SSLVERSION_MAX_LAST) + return CURLE_BAD_FUNCTION_ARGUMENT; + + primary->version = version; + primary->version_max = version_max; + } +#else + result = CURLE_UNKNOWN_OPTION; +#endif + break; + +#ifndef CURL_DISABLE_HTTP + case CURLOPT_AUTOREFERER: + /* + * Switch on automatic referer that gets set if curl follows locations. + */ + data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_ACCEPT_ENCODING: + /* + * String to use at the value of Accept-Encoding header. + * + * If the encoding is set to "" we use an Accept-Encoding header that + * encompasses all the encodings we support. + * If the encoding is set to NULL we don't send an Accept-Encoding header + * and ignore an received Content-Encoding header. + * + */ + argptr = va_arg(param, char *); + if(argptr && !*argptr) { + argptr = Curl_all_content_encodings(); + if(!argptr) + result = CURLE_OUT_OF_MEMORY; + else { + result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); + free(argptr); + } + } + else + result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); + break; + + case CURLOPT_TRANSFER_ENCODING: + data->set.http_transfer_encoding = (0 != va_arg(param, long)) ? + TRUE : FALSE; + break; + + case CURLOPT_FOLLOWLOCATION: + /* + * Follow Location: header hints on a HTTP-server. + */ + data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_UNRESTRICTED_AUTH: + /* + * Send authentication (user+password) when following locations, even when + * hostname changed. + */ + data->set.allow_auth_to_other_hosts = + (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_MAXREDIRS: + /* + * The maximum amount of hops you allow curl to follow Location: + * headers. This should mostly be used to detect never-ending loops. + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxredirs = arg; + break; + + case CURLOPT_POSTREDIR: + /* + * Set the behaviour of POST when redirecting + * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302 + * CURL_REDIR_POST_301 - POST is kept as POST after 301 + * CURL_REDIR_POST_302 - POST is kept as POST after 302 + * CURL_REDIR_POST_303 - POST is kept as POST after 303 + * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 + * other - POST is kept as POST after 301 and 302 + */ + arg = va_arg(param, long); + if(arg < CURL_REDIR_GET_ALL) + /* no return error on too high numbers since the bitmask could be + extended in a future */ + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.keep_post = arg & CURL_REDIR_POST_ALL; + break; + + case CURLOPT_POST: + /* Does this option serve a purpose anymore? Yes it does, when + CURLOPT_POSTFIELDS isn't used and the POST data is read off the + callback! */ + if(va_arg(param, long)) { + data->set.httpreq = HTTPREQ_POST; + data->set.opt_no_body = FALSE; /* this is implied */ + } + else + data->set.httpreq = HTTPREQ_GET; + break; + + case CURLOPT_COPYPOSTFIELDS: + /* + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. + * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to + * CURLOPT_COPYPOSTFIELDS and not altered later. + */ + argptr = va_arg(param, char *); + + if(!argptr || data->set.postfieldsize == -1) + result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); + else { + /* + * Check that requested length does not overflow the size_t type. + */ + + if((data->set.postfieldsize < 0) || + ((sizeof(curl_off_t) != sizeof(size_t)) && + (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) + result = CURLE_OUT_OF_MEMORY; + else { + char *p; + + (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + + /* Allocate even when size == 0. This satisfies the need of possible + later address compare to detect the COPYPOSTFIELDS mode, and + to mark that postfields is used rather than read function or + form data. + */ + p = malloc((size_t)(data->set.postfieldsize? + data->set.postfieldsize:1)); + + if(!p) + result = CURLE_OUT_OF_MEMORY; + else { + if(data->set.postfieldsize) + memcpy(p, argptr, (size_t)data->set.postfieldsize); + + data->set.str[STRING_COPYPOSTFIELDS] = p; + } + } + } + + data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; + data->set.httpreq = HTTPREQ_POST; + break; + + case CURLOPT_POSTFIELDS: + /* + * Like above, but use static data instead of copying it. + */ + data->set.postfields = va_arg(param, void *); + /* Release old copied data. */ + (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + data->set.httpreq = HTTPREQ_POST; + break; + + case CURLOPT_POSTFIELDSIZE: + /* + * The size of the POSTFIELD data to prevent libcurl to do strlen() to + * figure it out. Enables binary posts. + */ + bigsize = va_arg(param, long); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.postfieldsize < bigsize && + data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { + /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ + (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + data->set.postfields = NULL; + } + + data->set.postfieldsize = bigsize; + break; + + case CURLOPT_POSTFIELDSIZE_LARGE: + /* + * The size of the POSTFIELD data to prevent libcurl to do strlen() to + * figure it out. Enables binary posts. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.postfieldsize < bigsize && + data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { + /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ + (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + data->set.postfields = NULL; + } + + data->set.postfieldsize = bigsize; + break; + + case CURLOPT_HTTPPOST: + /* + * Set to make us do HTTP POST + */ + data->set.httppost = va_arg(param, struct curl_httppost *); + data->set.httpreq = HTTPREQ_POST_FORM; + data->set.opt_no_body = FALSE; /* this is implied */ + break; +#endif /* CURL_DISABLE_HTTP */ + + case CURLOPT_MIMEPOST: + /* + * Set to make us do MIME/form POST + */ + result = Curl_mime_set_subparts(&data->set.mimepost, + va_arg(param, curl_mime *), FALSE); + if(!result) { + data->set.httpreq = HTTPREQ_POST_MIME; + data->set.opt_no_body = FALSE; /* this is implied */ + } + break; + + case CURLOPT_REFERER: + /* + * String to set in the HTTP Referer: field. + */ + if(data->change.referer_alloc) { + Curl_safefree(data->change.referer); + data->change.referer_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], + va_arg(param, char *)); + data->change.referer = data->set.str[STRING_SET_REFERER]; + break; + + case CURLOPT_USERAGENT: + /* + * String to use in the HTTP User-Agent field + */ + result = Curl_setstropt(&data->set.str[STRING_USERAGENT], + va_arg(param, char *)); + break; + + case CURLOPT_HTTPHEADER: + /* + * Set a list with HTTP headers to use (or replace internals with) + */ + data->set.headers = va_arg(param, struct curl_slist *); + break; + +#ifndef CURL_DISABLE_HTTP + case CURLOPT_PROXYHEADER: + /* + * Set a list with proxy headers to use (or replace internals with) + * + * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a + * long time we remain doing it this way until CURLOPT_PROXYHEADER is + * used. As soon as this option has been used, if set to anything but + * NULL, custom headers for proxies are only picked from this list. + * + * Set this option to NULL to restore the previous behavior. + */ + data->set.proxyheaders = va_arg(param, struct curl_slist *); + break; + + case CURLOPT_HEADEROPT: + /* + * Set header option. + */ + arg = va_arg(param, long); + data->set.sep_headers = (arg & CURLHEADER_SEPARATE)? TRUE: FALSE; + break; + + case CURLOPT_HTTP200ALIASES: + /* + * Set a list of aliases for HTTP 200 in response header + */ + data->set.http200aliases = va_arg(param, struct curl_slist *); + break; + +#if !defined(CURL_DISABLE_COOKIES) + case CURLOPT_COOKIE: + /* + * Cookie string to send to the remote server in the request. + */ + result = Curl_setstropt(&data->set.str[STRING_COOKIE], + va_arg(param, char *)); + break; + + case CURLOPT_COOKIEFILE: + /* + * Set cookie file to read and parse. Can be used multiple times. + */ + argptr = (char *)va_arg(param, void *); + if(argptr) { + struct curl_slist *cl; + /* append the cookie file name to the list of file names, and deal with + them later */ + cl = curl_slist_append(data->change.cookielist, argptr); + if(!cl) { + curl_slist_free_all(data->change.cookielist); + data->change.cookielist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->change.cookielist = cl; /* store the list for later use */ + } + break; + + case CURLOPT_COOKIEJAR: + /* + * Set cookie file name to dump all cookies to when we're done. + */ + { + struct CookieInfo *newcookies; + result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], + va_arg(param, char *)); + + /* + * Activate the cookie parser. This may or may not already + * have been made. + */ + newcookies = Curl_cookie_init(data, NULL, data->cookies, + data->set.cookiesession); + if(!newcookies) + result = CURLE_OUT_OF_MEMORY; + data->cookies = newcookies; + } + break; + + case CURLOPT_COOKIESESSION: + /* + * Set this option to TRUE to start a new "cookie session". It will + * prevent the forthcoming read-cookies-from-file actions to accept + * cookies that are marked as being session cookies, as they belong to a + * previous session. + * + * In the original Netscape cookie spec, "session cookies" are cookies + * with no expire date set. RFC2109 describes the same action if no + * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds + * a 'Discard' action that can enforce the discard even for cookies that + * have a Max-Age. + * + * We run mostly with the original cookie spec, as hardly anyone implements + * anything else. + */ + data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_COOKIELIST: + argptr = va_arg(param, char *); + + if(argptr == NULL) + break; + + if(strcasecompare(argptr, "ALL")) { + /* clear all cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearall(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(argptr, "SESS")) { + /* clear session cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearsess(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(argptr, "FLUSH")) { + /* flush cookies to file, takes care of the locking */ + Curl_flush_cookies(data, 0); + } + else if(strcasecompare(argptr, "RELOAD")) { + /* reload cookies from file */ + Curl_cookie_loadfiles(data); + break; + } + else { + if(!data->cookies) + /* if cookie engine was not running, activate it */ + data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); + + argptr = strdup(argptr); + if(!argptr || !data->cookies) { + result = CURLE_OUT_OF_MEMORY; + free(argptr); + } + else { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + + if(checkprefix("Set-Cookie:", argptr)) + /* HTTP Header format line */ + Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); + + else + /* Netscape format line */ + Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); + + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + free(argptr); + } + } + + break; +#endif /* !CURL_DISABLE_COOKIES */ + + case CURLOPT_HTTPGET: + /* + * Set to force us do HTTP GET + */ + if(va_arg(param, long)) { + data->set.httpreq = HTTPREQ_GET; + data->set.upload = FALSE; /* switch off upload */ + data->set.opt_no_body = FALSE; /* this is implied */ + } + break; + + case CURLOPT_HTTP_VERSION: + /* + * This sets a requested HTTP version to be used. The value is one of + * the listed enums in curl/curl.h. + */ + arg = va_arg(param, long); + if(arg < CURL_HTTP_VERSION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef USE_NGHTTP2 + if(arg >= CURL_HTTP_VERSION_2) + return CURLE_UNSUPPORTED_PROTOCOL; +#else + if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + data->set.httpversion = arg; + break; + + case CURLOPT_EXPECT_100_TIMEOUT_MS: + /* + * Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.expect_100_timeout = arg; + break; + +#endif /* CURL_DISABLE_HTTP */ + + case CURLOPT_HTTPAUTH: + /* + * Set HTTP Authentication type BITMASK. + */ + { + int bitcheck; + bool authbits; + unsigned long auth = va_arg(param, unsigned long); + + if(auth == CURLAUTH_NONE) { + data->set.httpauth = auth; + break; + } + + /* the DIGEST_IE bit is only used to set a special marker, for all the + rest we need to handle it as normal DIGEST */ + data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE; + + if(auth & CURLAUTH_DIGEST_IE) { + auth |= CURLAUTH_DIGEST; /* set standard digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + } + + /* switch off bits we can't support */ +#ifndef USE_NTLM + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ + auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ +#elif !defined(NTLM_WB_ENABLED) + auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ +#endif +#ifndef USE_SPNEGO + auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without + GSS-API or SSPI */ +#endif + + /* check if any auth bit lower than CURLAUTH_ONLY is still set */ + bitcheck = 0; + authbits = FALSE; + while(bitcheck < 31) { + if(auth & (1UL << bitcheck++)) { + authbits = TRUE; + break; + } + } + if(!authbits) + return CURLE_NOT_BUILT_IN; /* no supported types left! */ + + data->set.httpauth = auth; + } + break; + + case CURLOPT_CUSTOMREQUEST: + /* + * Set a custom string to use as request + */ + result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], + va_arg(param, char *)); + + /* we don't set + data->set.httpreq = HTTPREQ_CUSTOM; + here, we continue as if we were using the already set type + and this just changes the actual request keyword */ + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_HTTPPROXYTUNNEL: + /* + * Tunnel operations through the proxy instead of normal proxy use + */ + data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ? + TRUE : FALSE; + break; + + case CURLOPT_PROXYPORT: + /* + * Explicitly set HTTP proxy port number. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxyport = arg; + break; + + case CURLOPT_PROXYAUTH: + /* + * Set HTTP Authentication type BITMASK. + */ + { + int bitcheck; + bool authbits; + unsigned long auth = va_arg(param, unsigned long); + + if(auth == CURLAUTH_NONE) { + data->set.proxyauth = auth; + break; + } + + /* the DIGEST_IE bit is only used to set a special marker, for all the + rest we need to handle it as normal DIGEST */ + data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE; + + if(auth & CURLAUTH_DIGEST_IE) { + auth |= CURLAUTH_DIGEST; /* set standard digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + } + /* switch off bits we can't support */ +#ifndef USE_NTLM + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ + auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ +#elif !defined(NTLM_WB_ENABLED) + auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ +#endif +#ifndef USE_SPNEGO + auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without + GSS-API or SSPI */ +#endif + + /* check if any auth bit lower than CURLAUTH_ONLY is still set */ + bitcheck = 0; + authbits = FALSE; + while(bitcheck < 31) { + if(auth & (1UL << bitcheck++)) { + authbits = TRUE; + break; + } + } + if(!authbits) + return CURLE_NOT_BUILT_IN; /* no supported types left! */ + + data->set.proxyauth = auth; + } + break; + + case CURLOPT_PROXY: + /* + * Set proxy server:port to use as proxy. + * + * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) + * we explicitly say that we don't want to use a proxy + * (even though there might be environment variables saying so). + * + * Setting it to NULL, means no proxy but allows the environment variables + * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). + */ + result = Curl_setstropt(&data->set.str[STRING_PROXY], + va_arg(param, char *)); + break; + + case CURLOPT_PRE_PROXY: + /* + * Set proxy server:port to use as SOCKS proxy. + * + * If the proxy is set to "" or NULL we explicitly say that we don't want + * to use the socks proxy. + */ + result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY], + va_arg(param, char *)); + break; + + case CURLOPT_PROXYTYPE: + /* + * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME + */ + arg = va_arg(param, long); + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxytype = (curl_proxytype)arg; + break; + + case CURLOPT_PROXY_TRANSFER_MODE: + /* + * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy + */ + switch(va_arg(param, long)) { + case 0: + data->set.proxy_transfer_mode = FALSE; + break; + case 1: + data->set.proxy_transfer_mode = TRUE; + break; + default: + /* reserve other values for future use */ + result = CURLE_UNKNOWN_OPTION; + break; + } + break; +#endif /* CURL_DISABLE_PROXY */ + + case CURLOPT_SOCKS5_AUTH: + data->set.socks5auth = va_arg(param, unsigned long); + if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + result = CURLE_NOT_BUILT_IN; + break; +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + case CURLOPT_SOCKS5_GSSAPI_NEC: + /* + * Set flag for NEC SOCK5 support + */ + data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_PROXY_SERVICE_NAME: + /* + * Set proxy authentication service name for Kerberos 5 and SPNEGO + */ + result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], + va_arg(param, char *)); + break; +#endif + +#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ + defined(USE_SPNEGO) + case CURLOPT_SERVICE_NAME: + /* + * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO + */ + result = Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], + va_arg(param, char *)); + break; + +#endif + + case CURLOPT_HEADERDATA: + /* + * Custom pointer to pass the header write callback function + */ + data->set.writeheader = (void *)va_arg(param, void *); + break; + case CURLOPT_ERRORBUFFER: + /* + * Error buffer provided by the caller to get the human readable + * error string in. + */ + data->set.errorbuffer = va_arg(param, char *); + break; + case CURLOPT_WRITEDATA: + /* + * FILE pointer to write to. Or possibly + * used as argument to the write callback. + */ + data->set.out = va_arg(param, void *); + break; + case CURLOPT_FTPPORT: + /* + * Use FTP PORT, this also specifies which IP address to use + */ + result = Curl_setstropt(&data->set.str[STRING_FTPPORT], + va_arg(param, char *)); + data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_USE_EPRT: + data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_USE_EPSV: + data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_USE_PRET: + data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_SSL_CCC: + arg = va_arg(param, long); + if((arg < CURLFTPSSL_CCC_NONE) || (arg > CURLFTPSSL_CCC_ACTIVE)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftp_ccc = (curl_ftpccc)arg; + break; + + case CURLOPT_FTP_SKIP_PASV_IP: + /* + * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the + * bypass of the IP address in PASV responses. + */ + data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_READDATA: + /* + * FILE pointer to read the file to be uploaded from. Or possibly + * used as argument to the read callback. + */ + data->set.in_set = va_arg(param, void *); + break; + case CURLOPT_INFILESIZE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.filesize = arg; + break; + case CURLOPT_INFILESIZE_LARGE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.filesize = bigsize; + break; + case CURLOPT_LOW_SPEED_LIMIT: + /* + * The low speed limit that if transfers are below this for + * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_limit = arg; + break; + case CURLOPT_MAX_SEND_SPEED_LARGE: + /* + * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * bytes per second the transfer is throttled.. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_send_speed = bigsize; + break; + case CURLOPT_MAX_RECV_SPEED_LARGE: + /* + * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per + * second the transfer is throttled.. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_recv_speed = bigsize; + break; + case CURLOPT_LOW_SPEED_TIME: + /* + * The low speed time that if transfers are below the set + * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_time = arg; + break; + case CURLOPT_URL: + /* + * The URL to fetch. + */ + if(data->change.url_alloc) { + /* the already set URL is allocated, free it first! */ + Curl_safefree(data->change.url); + data->change.url_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_URL], + va_arg(param, char *)); + data->change.url = data->set.str[STRING_SET_URL]; + break; + case CURLOPT_PORT: + /* + * The port number to use when getting the URL + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_port = arg; + break; + case CURLOPT_TIMEOUT: + /* + * The maximum time you allow curl to use for a single transfer + * operation. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.timeout = arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + + case CURLOPT_TIMEOUT_MS: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.timeout = arg; + break; + + case CURLOPT_CONNECTTIMEOUT: + /* + * The maximum time you allow curl to use to connect. + */ + arg = va_arg(param, long); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.connecttimeout = arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; + + case CURLOPT_CONNECTTIMEOUT_MS: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.connecttimeout = arg; + break; + + case CURLOPT_ACCEPTTIMEOUT_MS: + /* + * The maximum time you allow curl to wait for server connect + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.accepttimeout = arg; + break; + + case CURLOPT_USERPWD: + /* + * user:password to use in the operation + */ + result = setstropt_userpwd(va_arg(param, char *), + &data->set.str[STRING_USERNAME], + &data->set.str[STRING_PASSWORD]); + break; + + case CURLOPT_USERNAME: + /* + * authentication user name to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_USERNAME], + va_arg(param, char *)); + break; + + case CURLOPT_PASSWORD: + /* + * authentication password to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PASSWORD], + va_arg(param, char *)); + break; + + case CURLOPT_LOGIN_OPTIONS: + /* + * authentication options to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_OPTIONS], + va_arg(param, char *)); + break; + + case CURLOPT_XOAUTH2_BEARER: + /* + * OAuth 2.0 bearer token to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_BEARER], + va_arg(param, char *)); + break; + + case CURLOPT_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer + */ + data->set.postquote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PREQUOTE: + /* + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) + */ + data->set.prequote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_RESOLVE: + /* + * List of NAME:[address] names to populate the DNS cache with + * Prefix the NAME with dash (-) to _remove_ the name from the cache. + * + * Names added with this API will remain in the cache until explicitly + * removed or the handle is cleaned up. + * + * This API can remove any name from the DNS cache, but only entries + * that aren't actually in use right now will be pruned immediately. + */ + data->set.resolve = va_arg(param, struct curl_slist *); + data->change.resolve = data->set.resolve; + break; + case CURLOPT_PROGRESSFUNCTION: + /* + * Progress callback function + */ + data->set.fprogress = va_arg(param, curl_progress_callback); + if(data->set.fprogress) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ + break; + + case CURLOPT_XFERINFOFUNCTION: + /* + * Transfer info callback function + */ + data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); + if(data->set.fxferinfo) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ + + break; + + case CURLOPT_PROGRESSDATA: + /* + * Custom client data to pass to the progress callback + */ + data->set.progress_client = va_arg(param, void *); + break; + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYUSERPWD: + /* + * user:password needed to use the proxy + */ + result = setstropt_userpwd(va_arg(param, char *), + &data->set.str[STRING_PROXYUSERNAME], + &data->set.str[STRING_PROXYPASSWORD]); + break; + case CURLOPT_PROXYUSERNAME: + /* + * authentication user name to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], + va_arg(param, char *)); + break; + case CURLOPT_PROXYPASSWORD: + /* + * authentication password to use in the operation + */ + result = Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], + va_arg(param, char *)); + break; + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + result = Curl_setstropt(&data->set.str[STRING_NOPROXY], + va_arg(param, char *)); + break; +#endif + + case CURLOPT_RANGE: + /* + * What range of the file you want to transfer + */ + result = Curl_setstropt(&data->set.str[STRING_SET_RANGE], + va_arg(param, char *)); + break; + case CURLOPT_RESUME_FROM: + /* + * Resume transfer at the given file position + */ + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.set_resume_from = arg; + break; + case CURLOPT_RESUME_FROM_LARGE: + /* + * Resume transfer at the given file position + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.set_resume_from = bigsize; + break; + case CURLOPT_DEBUGFUNCTION: + /* + * stderr write callback. + */ + data->set.fdebug = va_arg(param, curl_debug_callback); + /* + * if the callback provided is NULL, it'll use the default callback + */ + break; + case CURLOPT_DEBUGDATA: + /* + * Set to a void * that should receive all error writes. This + * defaults to CURLOPT_STDERR for normal operations. + */ + data->set.debugdata = va_arg(param, void *); + break; + case CURLOPT_STDERR: + /* + * Set to a FILE * that should receive all error writes. This + * defaults to stderr for normal operations. + */ + data->set.err = va_arg(param, FILE *); + if(!data->set.err) + data->set.err = stderr; + break; + case CURLOPT_HEADERFUNCTION: + /* + * Set header write callback + */ + data->set.fwrite_header = va_arg(param, curl_write_callback); + break; + case CURLOPT_WRITEFUNCTION: + /* + * Set data write callback + */ + data->set.fwrite_func = va_arg(param, curl_write_callback); + if(!data->set.fwrite_func) { + data->set.is_fwrite_set = 0; + /* When set to NULL, reset to our internal default function */ + data->set.fwrite_func = (curl_write_callback)fwrite; + } + else + data->set.is_fwrite_set = 1; + break; + case CURLOPT_READFUNCTION: + /* + * Read data callback + */ + data->set.fread_func_set = va_arg(param, curl_read_callback); + if(!data->set.fread_func_set) { + data->set.is_fread_set = 0; + /* When set to NULL, reset to our internal default function */ + data->set.fread_func_set = (curl_read_callback)fread; + } + else + data->set.is_fread_set = 1; + break; + case CURLOPT_SEEKFUNCTION: + /* + * Seek callback. Might be NULL. + */ + data->set.seek_func = va_arg(param, curl_seek_callback); + break; + case CURLOPT_SEEKDATA: + /* + * Seek control callback. Might be NULL. + */ + data->set.seek_client = va_arg(param, void *); + break; + case CURLOPT_CONV_FROM_NETWORK_FUNCTION: + /* + * "Convert from network encoding" callback + */ + data->set.convfromnetwork = va_arg(param, curl_conv_callback); + break; + case CURLOPT_CONV_TO_NETWORK_FUNCTION: + /* + * "Convert to network encoding" callback + */ + data->set.convtonetwork = va_arg(param, curl_conv_callback); + break; + case CURLOPT_CONV_FROM_UTF8_FUNCTION: + /* + * "Convert from UTF-8 encoding" callback + */ + data->set.convfromutf8 = va_arg(param, curl_conv_callback); + break; + case CURLOPT_IOCTLFUNCTION: + /* + * I/O control callback. Might be NULL. + */ + data->set.ioctl_func = va_arg(param, curl_ioctl_callback); + break; + case CURLOPT_IOCTLDATA: + /* + * I/O control data pointer. Might be NULL. + */ + data->set.ioctl_client = va_arg(param, void *); + break; + case CURLOPT_SSLCERT: + /* + * String that holds file name of the SSL certificate to use + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLCERT: + /* + * String that holds file name of the SSL certificate to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_SSLKEY: + /* + * String that holds file name of the SSL key to use + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLKEY: + /* + * String that holds file name of the SSL key to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use for proxy + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_KEYPASSWD: + /* + * String that holds the SSL private key password for proxy. + */ + result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ + argptr = va_arg(param, char *); + if(argptr && argptr[0]) + result = Curl_ssl_set_engine(data, argptr); + break; + + case CURLOPT_SSLENGINE_DEFAULT: + /* + * flag to set engine as default. + */ + result = Curl_ssl_set_engine_default(data); + break; + case CURLOPT_CRLF: + /* + * Kludgy option to enable CRLF conversions. Subject for removal. + */ + data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_INTERFACE: + /* + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. + */ + result = Curl_setstropt(&data->set.str[STRING_DEVICE], + va_arg(param, char *)); + break; + case CURLOPT_LOCALPORT: + /* + * Set what local port to bind the socket to when performing an operation. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localport = curlx_sltous(arg); + break; + case CURLOPT_LOCALPORTRANGE: + /* + * Set number of local ports to try, starting with CURLOPT_LOCALPORT. + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localportrange = curlx_sltosi(arg); + break; + case CURLOPT_KRBLEVEL: + /* + * A string that defines the kerberos security level. + */ + result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], + va_arg(param, char *)); + data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; + break; + case CURLOPT_GSSAPI_DELEGATION: + /* + * GSS-API credential delegation bitmask + */ + arg = va_arg(param, long); + if(arg < CURLGSSAPI_DELEGATION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.gssapi_delegation = arg; + break; + case CURLOPT_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying. + */ + data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ? + TRUE : FALSE; + + /* Update the current connection ssl_config. */ + if(data->easy_conn) { + data->easy_conn->ssl_config.verifypeer = + data->set.ssl.primary.verifypeer; + } + break; + case CURLOPT_PROXY_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying for proxy. + */ + data->set.proxy_ssl.primary.verifypeer = + (0 != va_arg(param, long))?TRUE:FALSE; + + /* Update the current connection proxy_ssl_config. */ + if(data->easy_conn) { + data->easy_conn->proxy_ssl_config.verifypeer = + data->set.proxy_ssl.primary.verifypeer; + } + break; + case CURLOPT_SSL_VERIFYHOST: + /* + * Enable verification of the host name in the peer certificate + */ + arg = va_arg(param, long); + + /* Obviously people are not reading documentation and too many thought + this argument took a boolean when it wasn't and misused it. We thus ban + 1 as a sensible input and we warn about its use. Then we only have the + 2 action internally stored as TRUE. */ + + if(1 == arg) { + failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE; + + /* Update the current connection ssl_config. */ + if(data->easy_conn) { + data->easy_conn->ssl_config.verifyhost = + data->set.ssl.primary.verifyhost; + } + break; + case CURLOPT_PROXY_SSL_VERIFYHOST: + /* + * Enable verification of the host name in the peer certificate for proxy + */ + arg = va_arg(param, long); + + /* Obviously people are not reading documentation and too many thought + this argument took a boolean when it wasn't and misused it. We thus ban + 1 as a sensible input and we warn about its use. Then we only have the + 2 action internally stored as TRUE. */ + + if(1 == arg) { + failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE; + + /* Update the current connection proxy_ssl_config. */ + if(data->easy_conn) { + data->easy_conn->proxy_ssl_config.verifyhost = + data->set.proxy_ssl.primary.verifyhost; + } + break; + case CURLOPT_SSL_VERIFYSTATUS: + /* + * Enable certificate status verifying. + */ + if(!Curl_ssl_cert_status_request()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ? + TRUE : FALSE; + + /* Update the current connection ssl_config. */ + if(data->easy_conn) { + data->easy_conn->ssl_config.verifystatus = + data->set.ssl.primary.verifystatus; + } + break; + case CURLOPT_SSL_CTX_FUNCTION: + /* + * Set a SSL_CTX callback + */ +#ifdef USE_SSL + if(Curl_ssl->have_ssl_ctx) + data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_SSL_CTX_DATA: + /* + * Set a SSL_CTX callback parameter pointer + */ +#ifdef USE_SSL + if(Curl_ssl->have_ssl_ctx) + data->set.ssl.fsslctxp = va_arg(param, void *); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_SSL_FALSESTART: + /* + * Enable TLS false start. + */ + if(!Curl_ssl_false_start()) { + result = CURLE_NOT_BUILT_IN; + break; + } + + data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_CERTINFO: +#ifdef USE_SSL + if(Curl_ssl->have_certinfo) + data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify file name of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl->have_pinnedpubkey) + result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_PROXY_PINNEDPUBLICKEY: + /* + * Set pinned public key for SSL connection. + * Specify file name of the public key in DER format. + */ +#ifdef USE_SSL + if(Curl_ssl->have_pinnedpubkey) + result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify file name of the CA certificate + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_CAINFO: + /* + * Set CA info SSL connection for proxy. Specify file name of the + * CA certificate + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_CAPATH: + /* + * Set CA path info for SSL connection. Specify directory name of the CA + * certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl->have_ca_path) + /* This does not work on windows. */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_ORIG], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_PROXY_CAPATH: + /* + * Set CA path info for SSL connection proxy. Specify directory name of the + * CA certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl->have_ca_path) + /* This does not work on windows. */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], + va_arg(param, char *)); + else +#endif + result = CURLE_NOT_BUILT_IN; + break; + case CURLOPT_CRLFILE: + /* + * Set CRL file info for SSL connection. Specify file name of the CRL + * to check certificates revocation + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_PROXY_CRLFILE: + /* + * Set CRL file info for SSL connection for proxy. Specify file name of the + * CRL to check certificates revocation + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], + va_arg(param, char *)); + break; + case CURLOPT_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG], + va_arg(param, char *)); + break; + case CURLOPT_TELNETOPTIONS: + /* + * Set a linked list of telnet options + */ + data->set.telnet_options = va_arg(param, struct curl_slist *); + break; + + case CURLOPT_BUFFERSIZE: + /* + * The application kindly asks for a differently sized receive buffer. + * If it seems reasonable, we'll use it. + */ + arg = va_arg(param, long); + + if(arg > READBUFFER_MAX) + arg = READBUFFER_MAX; + else if(arg < 1) + arg = READBUFFER_SIZE; + else if(arg < READBUFFER_MIN) + arg = READBUFFER_MIN; + + /* Resize if new size */ + if(arg != data->set.buffer_size) { + char *newbuff = realloc(data->state.buffer, arg + 1); + if(!newbuff) { + DEBUGF(fprintf(stderr, "Error: realloc of buffer failed\n")); + result = CURLE_OUT_OF_MEMORY; + } + else + data->state.buffer = newbuff; + } + data->set.buffer_size = arg; + + break; + + case CURLOPT_NOSIGNAL: + /* + * The application asks not to set any signal() or alarm() handlers, + * even when using a timeout. + */ + data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_SHARE: + { + struct Curl_share *set; + set = va_arg(param, struct Curl_share *); + + /* disconnect from old share, if any */ + if(data->share) { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + if(data->dns.hostcachetype == HCACHE_SHARED) { + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies == data->cookies) + data->cookies = NULL; +#endif + + if(data->share->sslsession == data->state.session) + data->state.session = NULL; + + data->share->dirty--; + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + data->share = NULL; + } + + /* use new share if it set */ + data->share = set; + if(data->share) { + + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + data->share->dirty++; + + if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) { + /* use shared host cache */ + data->dns.hostcache = &data->share->hostcache; + data->dns.hostcachetype = HCACHE_SHARED; + } +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = data->share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ + if(data->share->sslsession) { + data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; + data->state.session = data->share->sslsession; + } + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + + } + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + } + break; + + case CURLOPT_PRIVATE: + /* + * Set private data pointer. + */ + data->set.private_data = va_arg(param, void *); + break; + + case CURLOPT_MAXFILESIZE: + /* + * Set the maximum size of a file to download. + */ + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_filesize = arg; + break; + +#ifdef USE_SSL + case CURLOPT_USE_SSL: + /* + * Make transfers attempt to use SSL/TLS. + */ + arg = va_arg(param, long); + if((arg < CURLUSESSL_NONE) || (arg > CURLUSESSL_ALL)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_ssl = (curl_usessl)arg; + break; + + case CURLOPT_SSL_OPTIONS: + arg = va_arg(param, long); + data->set.ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; + data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + break; + + case CURLOPT_PROXY_SSL_OPTIONS: + arg = va_arg(param, long); + data->set.proxy_ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; + data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + break; + +#endif + case CURLOPT_FTPSSLAUTH: + /* + * Set a specific auth for FTP-SSL transfers. + */ + arg = va_arg(param, long); + if((arg < CURLFTPAUTH_DEFAULT) || (arg > CURLFTPAUTH_TLS)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftpsslauth = (curl_ftpauth)arg; + break; + + case CURLOPT_IPRESOLVE: + arg = va_arg(param, long); + if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ipver = arg; + break; + + case CURLOPT_MAXFILESIZE_LARGE: + /* + * Set the maximum size of a file to download. + */ + bigsize = va_arg(param, curl_off_t); + if(bigsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_filesize = bigsize; + break; + + case CURLOPT_TCP_NODELAY: + /* + * Enable or disable TCP_NODELAY, which will disable/enable the Nagle + * algorithm + */ + data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_ACCOUNT: + result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], + va_arg(param, char *)); + break; + + case CURLOPT_IGNORE_CONTENT_LENGTH: + data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_CONNECT_ONLY: + /* + * No data transfer, set up connection and let application use the socket + */ + data->set.connect_only = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], + va_arg(param, char *)); + break; + + case CURLOPT_SOCKOPTFUNCTION: + /* + * socket callback function: called after socket() but before connect() + */ + data->set.fsockopt = va_arg(param, curl_sockopt_callback); + break; + + case CURLOPT_SOCKOPTDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.sockopt_client = va_arg(param, void *); + break; + + case CURLOPT_OPENSOCKETFUNCTION: + /* + * open/create socket callback function: called instead of socket(), + * before connect() + */ + data->set.fopensocket = va_arg(param, curl_opensocket_callback); + break; + + case CURLOPT_OPENSOCKETDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.opensocket_client = va_arg(param, void *); + break; + + case CURLOPT_CLOSESOCKETFUNCTION: + /* + * close socket callback function: called instead of close() + * when shutting down a connection + */ + data->set.fclosesocket = va_arg(param, curl_closesocket_callback); + break; + + case CURLOPT_CLOSESOCKETDATA: + /* + * socket callback data pointer. Might be NULL. + */ + data->set.closesocket_client = va_arg(param, void *); + break; + + case CURLOPT_SSL_SESSIONID_CACHE: + data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? + TRUE : FALSE; + data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; + break; + +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) + /* we only include SSH options if explicitly built to support SSH */ + case CURLOPT_SSH_AUTH_TYPES: + data->set.ssh_auth_types = va_arg(param, long); + break; + + case CURLOPT_SSH_PUBLIC_KEYFILE: + /* + * Use this file instead of the $HOME/.ssh/id_dsa.pub file + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], + va_arg(param, char *)); + break; + + case CURLOPT_SSH_PRIVATE_KEYFILE: + /* + * Use this file instead of the $HOME/.ssh/id_dsa file + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], + va_arg(param, char *)); + break; + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: + /* + * Option to allow for the MD5 of the host public key to be checked + * for validation purposes. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], + va_arg(param, char *)); + break; +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + case CURLOPT_SSH_KNOWNHOSTS: + /* + * Store the file name to read known hosts from. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], + va_arg(param, char *)); + break; + + case CURLOPT_SSH_KEYFUNCTION: + /* setting to NULL is fine since the ssh.c functions themselves will + then rever to use the internal default */ + data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); + break; + + case CURLOPT_SSH_KEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + data->set.ssh_keyfunc_userp = va_arg(param, void *); + break; +#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ +#endif /* USE_LIBSSH2 */ + + case CURLOPT_HTTP_TRANSFER_DECODING: + /* + * disable libcurl transfer encoding is used + */ + data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_HTTP_CONTENT_DECODING: + /* + * raw data passed to the application when content encoding is used + */ + data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_NEW_FILE_PERMS: + /* + * Uses these permissions instead of 0644 + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 0777)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.new_file_perms = arg; + break; + + case CURLOPT_NEW_DIRECTORY_PERMS: + /* + * Uses these permissions instead of 0755 + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 0777)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.new_directory_perms = arg; + break; + + case CURLOPT_ADDRESS_SCOPE: + /* + * We always get longs when passed plain numericals, but for this value we + * know that an unsigned int will always hold the value so we blindly + * typecast to this type + */ + arg = va_arg(param, long); + if((arg < 0) || (arg > 0xf)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.scope_id = curlx_sltoui(arg); + break; + + case CURLOPT_PROTOCOLS: + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + data->set.allowed_protocols = va_arg(param, long); + break; + + case CURLOPT_REDIR_PROTOCOLS: + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + data->set.redir_protocols = va_arg(param, long); + break; + + case CURLOPT_DEFAULT_PROTOCOL: + /* Set the protocol to use when the URL doesn't include any protocol */ + result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], + va_arg(param, char *)); + break; + + case CURLOPT_MAIL_FROM: + /* Set the SMTP mail originator */ + result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM], + va_arg(param, char *)); + break; + + case CURLOPT_MAIL_AUTH: + /* Set the SMTP auth originator */ + result = Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], + va_arg(param, char *)); + break; + + case CURLOPT_MAIL_RCPT: + /* Set the list of mail recipients */ + data->set.mail_rcpt = va_arg(param, struct curl_slist *); + break; + + case CURLOPT_SASL_IR: + /* Enable/disable SASL initial response */ + data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + + case CURLOPT_RTSP_REQUEST: + { + /* + * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) + * Would this be better if the RTSPREQ_* were just moved into here? + */ + long curl_rtspreq = va_arg(param, long); + Curl_RtspReq rtspreq = RTSPREQ_NONE; + switch(curl_rtspreq) { + case CURL_RTSPREQ_OPTIONS: + rtspreq = RTSPREQ_OPTIONS; + break; + + case CURL_RTSPREQ_DESCRIBE: + rtspreq = RTSPREQ_DESCRIBE; + break; + + case CURL_RTSPREQ_ANNOUNCE: + rtspreq = RTSPREQ_ANNOUNCE; + break; + + case CURL_RTSPREQ_SETUP: + rtspreq = RTSPREQ_SETUP; + break; + + case CURL_RTSPREQ_PLAY: + rtspreq = RTSPREQ_PLAY; + break; + + case CURL_RTSPREQ_PAUSE: + rtspreq = RTSPREQ_PAUSE; + break; + + case CURL_RTSPREQ_TEARDOWN: + rtspreq = RTSPREQ_TEARDOWN; + break; + + case CURL_RTSPREQ_GET_PARAMETER: + rtspreq = RTSPREQ_GET_PARAMETER; + break; + + case CURL_RTSPREQ_SET_PARAMETER: + rtspreq = RTSPREQ_SET_PARAMETER; + break; + + case CURL_RTSPREQ_RECORD: + rtspreq = RTSPREQ_RECORD; + break; + + case CURL_RTSPREQ_RECEIVE: + rtspreq = RTSPREQ_RECEIVE; + break; + default: + rtspreq = RTSPREQ_NONE; + } + + data->set.rtspreq = rtspreq; + break; + } + + + case CURLOPT_RTSP_SESSION_ID: + /* + * Set the RTSP Session ID manually. Useful if the application is + * resuming a previously established RTSP session + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_SESSION_ID], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_STREAM_URI: + /* + * Set the Stream URI for the RTSP request. Unless the request is + * for generic server options, the application will need to set this. + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_TRANSPORT: + /* + * The content of the Transport: header for the RTSP request + */ + result = Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], + va_arg(param, char *)); + break; + + case CURLOPT_RTSP_CLIENT_CSEQ: + /* + * Set the CSEQ number to issue for the next RTSP request. Useful if the + * application is resuming a previously broken connection. The CSEQ + * will increment from this new number henceforth. + */ + data->state.rtsp_next_client_CSeq = va_arg(param, long); + break; + + case CURLOPT_RTSP_SERVER_CSEQ: + /* Same as the above, but for server-initiated requests */ + data->state.rtsp_next_client_CSeq = va_arg(param, long); + break; + + case CURLOPT_INTERLEAVEDATA: + data->set.rtp_out = va_arg(param, void *); + break; + case CURLOPT_INTERLEAVEFUNCTION: + /* Set the user defined RTP write function */ + data->set.fwrite_rtp = va_arg(param, curl_write_callback); + break; + + case CURLOPT_WILDCARDMATCH: + data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_CHUNK_BGN_FUNCTION: + data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); + break; + case CURLOPT_CHUNK_END_FUNCTION: + data->set.chunk_end = va_arg(param, curl_chunk_end_callback); + break; + case CURLOPT_FNMATCH_FUNCTION: + data->set.fnmatch = va_arg(param, curl_fnmatch_callback); + break; + case CURLOPT_CHUNK_DATA: + data->wildcard.customptr = va_arg(param, void *); + break; + case CURLOPT_FNMATCH_DATA: + data->set.fnmatch_data = va_arg(param, void *); + break; +#ifdef USE_TLS_SRP + case CURLOPT_TLSAUTH_USERNAME: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_ORIG], + va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_ORIG] && !data->set.ssl.authtype) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_PROXY_TLSAUTH_USERNAME: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], + va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && + !data->set.proxy_ssl.authtype) + data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_TLSAUTH_PASSWORD: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_ORIG], + va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_ORIG] && !data->set.ssl.authtype) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_PROXY_TLSAUTH_PASSWORD: + result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], + va_arg(param, char *)); + if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && + !data->set.proxy_ssl.authtype) + data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ + break; + case CURLOPT_TLSAUTH_TYPE: + argptr = va_arg(param, char *); + if(!argptr || + strncasecompare(argptr, "SRP", strlen("SRP"))) + data->set.ssl.authtype = CURL_TLSAUTH_SRP; + else + data->set.ssl.authtype = CURL_TLSAUTH_NONE; + break; + case CURLOPT_PROXY_TLSAUTH_TYPE: + argptr = va_arg(param, char *); + if(!argptr || + strncasecompare(argptr, "SRP", strlen("SRP"))) + data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; + else + data->set.proxy_ssl.authtype = CURL_TLSAUTH_NONE; + break; +#endif + case CURLOPT_DNS_SERVERS: + result = Curl_set_dns_servers(data, va_arg(param, char *)); + break; + case CURLOPT_DNS_INTERFACE: + result = Curl_set_dns_interface(data, va_arg(param, char *)); + break; + case CURLOPT_DNS_LOCAL_IP4: + result = Curl_set_dns_local_ip4(data, va_arg(param, char *)); + break; + case CURLOPT_DNS_LOCAL_IP6: + result = Curl_set_dns_local_ip6(data, va_arg(param, char *)); + break; + + case CURLOPT_TCP_KEEPALIVE: + data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_TCP_KEEPIDLE: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.tcp_keepidle = arg; + break; + case CURLOPT_TCP_KEEPINTVL: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.tcp_keepintvl = arg; + break; + case CURLOPT_TCP_FASTOPEN: +#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ + defined(TCP_FASTOPEN_CONNECT) + data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE; +#else + result = CURLE_NOT_BUILT_IN; +#endif + break; + case CURLOPT_SSL_ENABLE_NPN: + data->set.ssl_enable_npn = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_SSL_ENABLE_ALPN: + data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + data->set.abstract_unix_socket = FALSE; + result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], + va_arg(param, char *)); + break; + case CURLOPT_ABSTRACT_UNIX_SOCKET: + data->set.abstract_unix_socket = TRUE; + result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], + va_arg(param, char *)); + break; +#endif + + case CURLOPT_PATH_AS_IS: + data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_PIPEWAIT: + data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_STREAM_WEIGHT: +#ifndef USE_NGHTTP2 + return CURLE_NOT_BUILT_IN; +#else + arg = va_arg(param, long); + if((arg >= 1) && (arg <= 256)) + data->set.stream_weight = (int)arg; + break; +#endif + case CURLOPT_STREAM_DEPENDS: + case CURLOPT_STREAM_DEPENDS_E: + { +#ifndef USE_NGHTTP2 + return CURLE_NOT_BUILT_IN; +#else + struct Curl_easy *dep = va_arg(param, struct Curl_easy *); + if(!dep || GOOD_EASY_HANDLE(dep)) { + if(data->set.stream_depends_on) { + Curl_http2_remove_child(data->set.stream_depends_on, data); + } + Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); + } + break; +#endif + } + case CURLOPT_CONNECT_TO: + data->set.connect_to = va_arg(param, struct curl_slist *); + break; + case CURLOPT_SUPPRESS_CONNECT_HEADERS: + data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE; + break; + case CURLOPT_SSH_COMPRESSION: + data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE; + break; + default: + /* unknown tag and its companion, just ignore: */ + result = CURLE_UNKNOWN_OPTION; + break; + } + + return result; +} + +/* + * curl_easy_setopt() is the external interface for setting options on an + * easy handle. + */ + +#undef curl_easy_setopt +CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) +{ + va_list arg; + CURLcode result; + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + + va_start(arg, tag); + + result = Curl_vsetopt(data, tag, arg); + + va_end(arg); + return result; +} diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h new file mode 100644 index 0000000..c658e04 --- /dev/null +++ b/Utilities/cmcurl/lib/setopt.h @@ -0,0 +1,29 @@ +#ifndef HEADER_CURL_SETOPT_H +#define HEADER_CURL_SETOPT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +CURLcode Curl_setstropt(char **charp, const char *s); +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, + va_list arg); + +#endif /* HEADER_CURL_SETOPT_H */ diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c new file mode 100644 index 0000000..cd81c02 --- /dev/null +++ b/Utilities/cmcurl/lib/sha256.c @@ -0,0 +1,262 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, Florin Petriuc, <petriuc.florin@gmail.com> + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifndef CURL_DISABLE_CRYPTO_AUTH + +#include "warnless.h" +#include "curl_sha256.h" + +#if defined(USE_OPENSSL) + +/* When OpenSSL is available we use the SHA256-function from OpenSSL */ +#include <openssl/sha.h> + +#else + +/* When no other crypto library is available we use this code segment */ + +/* ===== start - public domain SHA256 implementation ===== */ +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define WPA_GET_BE32(a) ((((unsigned long)(a)[0]) << 24) | \ + (((unsigned long)(a)[1]) << 16) | \ + (((unsigned long)(a)[2]) << 8) | \ + ((unsigned long)(a)[3])) +#define WPA_PUT_BE32(a, val) \ +do { \ + (a)[0] = (unsigned char)((((unsigned long) (val)) >> 24) & 0xff); \ + (a)[1] = (unsigned char)((((unsigned long) (val)) >> 16) & 0xff); \ + (a)[2] = (unsigned char)((((unsigned long) (val)) >> 8) & 0xff); \ + (a)[3] = (unsigned char)(((unsigned long) (val)) & 0xff); \ +} while(0) + +#ifdef HAVE_LONGLONG +#define WPA_PUT_BE64(a, val) \ +do { \ + (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \ + (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \ + (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \ + (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \ + (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \ + (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \ + (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \ + (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \ +} while(0) +#else +#define WPA_PUT_BE64(a, val) \ +do { \ + (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \ + (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \ + (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \ + (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \ + (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \ + (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \ + (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \ + (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \ +} while(0) +#endif + +typedef struct sha256_state { +#ifdef HAVE_LONGLONG + unsigned long long length; +#else + unsigned __int64 length; +#endif + unsigned long state[8], curlen; + unsigned char buf[64]; +} SHA256_CTX; +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; +/* Various logical functions */ +#define RORc(x, y) \ +(((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ + ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, + unsigned char *buf) +{ + unsigned long S[8], W[64], t0, t1; + unsigned long t; + int i; + /* copy state into S */ + for(i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + /* copy the state into 512-bits into W[0..15] */ + for(i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + /* fill W[16..63] */ + for(i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + for(i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + /* feedback */ + for(i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} +/* Initialize the hash state */ +static void SHA256_Init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +static int SHA256_Update(struct sha256_state *md, + const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; +#define block_size 64 + if(md->curlen > sizeof(md->buf)) + return -1; + while(inlen > 0) { + if(md->curlen == 0 && inlen >= block_size) { + if(sha256_compress(md, (unsigned char *)in) < 0) + return -1; + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } + else { + n = MIN(inlen, (block_size - md->curlen)); + memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if(md->curlen == block_size) { + if(sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + return 0; +} +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +static int SHA256_Final(unsigned char *out, + struct sha256_state *md) +{ + int i; + if(md->curlen >= sizeof(md->buf)) + return -1; + /* increase the length of the message */ + md->length += md->curlen * 8; + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if(md->curlen > 56) { + while(md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + /* pad upto 56 bytes of zeroes */ + while(md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + /* copy output */ + for(i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + return 0; +} +/* ===== end - public domain SHA256 implementation ===== */ + +#endif + +void Curl_sha256it(unsigned char *outbuffer, /* 32 unsigned chars */ + const unsigned char *input) +{ + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, input, curlx_uztoui(strlen((char *)input))); + SHA256_Final(outbuffer, &ctx); +} + +#endif /* CURL_DISABLE_CRYPTO_AUTH */ diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c index 5b3957f..870b191 100644 --- a/Utilities/cmcurl/lib/share.c +++ b/Utilities/cmcurl/lib/share.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -102,6 +102,8 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) break; case CURL_LOCK_DATA_CONNECT: /* not supported (yet) */ + if(Curl_conncache_init(&share->conn_cache, 103)) + res = CURLSHE_NOMEM; break; default: @@ -186,6 +188,8 @@ curl_share_cleanup(struct Curl_share *share) return CURLSHE_IN_USE; } + Curl_conncache_close_all_connections(&share->conn_cache); + Curl_conncache_destroy(&share->conn_cache); Curl_hash_destroy(&share->hostcache); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h index c039a16..4b13406 100644 --- a/Utilities/cmcurl/lib/share.h +++ b/Utilities/cmcurl/lib/share.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,7 @@ #include <curl/curl.h> #include "cookie.h" #include "urldata.h" +#include "conncache.h" /* SalfordC says "A structure member may not be volatile". Hence: */ @@ -43,7 +44,7 @@ struct Curl_share { curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; - + struct conncache conn_cache; struct curl_hash hostcache; #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) struct CookieInfo *cookies; diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 13dfd51..6cb4083 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -6,7 +6,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies - * Copyright (C) 2016-2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2016-2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -146,19 +146,12 @@ static unsigned int smb_swap32(unsigned int x) ((x >> 24) & 0xff); } -#ifdef HAVE_LONGLONG -static unsigned long long smb_swap64(unsigned long long x) +static curl_off_t smb_swap64(curl_off_t x) { - return ((unsigned long long) smb_swap32((unsigned int) x) << 32) | + return ((curl_off_t) smb_swap32((unsigned int) x) << 32) | smb_swap32((unsigned int) (x >> 32)); } -#else -static unsigned __int64 smb_swap64(unsigned __int64 x) -{ - return ((unsigned __int64) smb_swap32((unsigned int) x) << 32) | - smb_swap32((unsigned int) (x >> 32)); -} -#endif + #else # define smb_swap16(x) (x) # define smb_swap32(x) (x) @@ -648,7 +641,7 @@ static CURLcode smb_connection_state(struct connectdata *conn, bool *done) if(smbc->state == SMB_CONNECTING) { #ifdef USE_SSL if((conn->handler->flags & PROTOPT_SSL)) { - bool ssl_done; + bool ssl_done = FALSE; result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &ssl_done); if(result && result != CURLE_AGAIN) return result; @@ -719,17 +712,11 @@ static CURLcode smb_connection_state(struct connectdata *conn, bool *done) * Convert a timestamp from the Windows world (100 nsec units from * 1 Jan 1601) to Posix time. */ -static void get_posix_time(long *_out, const void *_in) +static void get_posix_time(long *out, curl_off_t timestamp) { -#ifdef HAVE_LONGLONG - long long timestamp = *(long long *) _in; -#else - unsigned __int64 timestamp = *(unsigned __int64 *) _in; -#endif - - timestamp -= 116444736000000000ULL; + timestamp -= 116444736000000000; timestamp /= 10000000; - *_out = (long) timestamp; + *out = (long) timestamp; } static CURLcode smb_request_state(struct connectdata *conn, bool *done) @@ -798,7 +785,7 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done) conn->data->req.size = smb_swap64(smb_m->end_of_file); Curl_pgrsSetDownloadSize(conn->data, conn->data->req.size); if(conn->data->set.get_filetime) - get_posix_time(&conn->data->info.filetime, &smb_m->last_change_time); + get_posix_time(&conn->data->info.filetime, smb_m->last_change_time); next_state = SMB_DOWNLOAD; } break; diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index 1a4f66e..c3ee7ae 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h @@ -8,6 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -165,11 +166,7 @@ struct smb_nt_create { unsigned int flags; unsigned int root_fid; unsigned int access; -#ifdef HAVE_LONGLONG - unsigned long long allocation_size; -#else - unsigned __int64 allocation_size; -#endif + curl_off_t allocation_size; unsigned int ext_file_attributes; unsigned int share_access; unsigned int create_disposition; @@ -187,25 +184,15 @@ struct smb_nt_create_response { unsigned char op_lock_level; unsigned short fid; unsigned int create_disposition; -#ifdef HAVE_LONGLONG - unsigned long long create_time; - unsigned long long last_access_time; - unsigned long long last_write_time; - unsigned long long last_change_time; -#else - unsigned __int64 create_time; - unsigned __int64 last_access_time; - unsigned __int64 last_write_time; - unsigned __int64 last_change_time; -#endif + + curl_off_t create_time; + curl_off_t last_access_time; + curl_off_t last_write_time; + curl_off_t last_change_time; unsigned int ext_file_attributes; -#ifdef HAVE_LONGLONG - unsigned long long allocation_size; - unsigned long long end_of_file; -#else - unsigned __int64 allocation_size; - unsigned __int64 end_of_file; -#endif + curl_off_t allocation_size; + curl_off_t end_of_file; + } PACK; struct smb_read { diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index de2dd33..d9f1a85 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -232,23 +232,30 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, */ static void smtp_get_message(char *buffer, char **outptr) { - size_t len = 0; + size_t len = strlen(buffer); char *message = NULL; - /* Find the start of the message */ - for(message = buffer + 4; *message == ' ' || *message == '\t'; message++) - ; - - /* Find the end of the message */ - for(len = strlen(message); len--;) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; - - /* Terminate the message */ - if(++len) { - message[len] = '\0'; + if(len > 4) { + /* Find the start of the message */ + len -= 4; + for(message = buffer + 4; *message == ' ' || *message == '\t'; + message++, len--) + ; + + /* Find the end of the message */ + for(; len--;) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; + + /* Terminate the message */ + if(++len) { + message[len] = '\0'; + } } + else + /* junk input => zero length output */ + message = &buffer[len]; *outptr = message; } @@ -1188,6 +1195,9 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, if(!smtp || !pp->conn) return CURLE_OK; + /* Cleanup our per-request based variables */ + Curl_safefree(smtp->custom); + if(status) { connclose(conn, "SMTP done with bad status"); /* marked for closure */ result = status; /* use the already set error code */ @@ -1230,7 +1240,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, } else { /* Successfully sent so adjust the response timeout relative to now */ - pp->response = Curl_tvnow(); + pp->response = Curl_now(); free(eob); } @@ -1246,9 +1256,6 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, result = smtp_block_statemach(conn); } - /* Cleanup our per-request based variables */ - Curl_safefree(smtp->custom); - /* Clear the transfer mode for the next request */ smtp->transfer = FTPTRANSFER_BODY; diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index e64cb98..ac4270e 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -57,7 +57,7 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */ ssize_t nread; ssize_t allread = 0; int result; - time_t timeleft; + timediff_t timeleft; *n = 0; for(;;) { timeleft = Curl_timeleft(conn->data, NULL, TRUE); @@ -382,7 +382,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, CURLcode code; curl_socket_t sock = conn->sock[sockindex]; struct Curl_easy *data = conn->data; - time_t timeout; + timediff_t timeout; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; const size_t hostname_len = strlen(hostname); diff --git a/Utilities/cmcurl/lib/speedcheck.c b/Utilities/cmcurl/lib/speedcheck.c index fe669f1..3aeea91 100644 --- a/Utilities/cmcurl/lib/speedcheck.c +++ b/Utilities/cmcurl/lib/speedcheck.c @@ -46,7 +46,7 @@ CURLcode Curl_speedcheck(struct Curl_easy *data, data->state.keeps_speed = now; else { /* how long has it been under the limit */ - time_t howlong = Curl_tvdiff(now, data->state.keeps_speed); + timediff_t howlong = Curl_timediff(now, data->state.keeps_speed); if(howlong >= data->set.low_speed_time * 1000) { /* too long */ diff --git a/Utilities/cmcurl/lib/ssh-libssh.c b/Utilities/cmcurl/lib/ssh-libssh.c new file mode 100644 index 0000000..56775d7 --- /dev/null +++ b/Utilities/cmcurl/lib/ssh-libssh.c @@ -0,0 +1,2733 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, + * Robert Kolcun, Andreas Schneider + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_LIBSSH + +#include <limits.h> + +#include <libssh/libssh.h> +#include <libssh/sftp.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_UTSNAME_H +#include <sys/utsname.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef __VMS +#include <in.h> +#include <inet.h> +#endif + +#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) +#undef in_addr_t +#define in_addr_t unsigned long +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "hostip.h" +#include "progress.h" +#include "transfer.h" +#include "escape.h" +#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "ssh.h" +#include "url.h" +#include "speedcheck.h" +#include "getinfo.h" +#include "strdup.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "connect.h" +#include "strerror.h" +#include "inet_ntop.h" +#include "parsedate.h" /* for the week day and month names */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "strtoofft.h" +#include "multiif.h" +#include "select.h" +#include "warnless.h" + +/* for permission and open flags */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" +#include "curl_path.h" + +/* Local functions: */ +static CURLcode myssh_connect(struct connectdata *conn, bool *done); +static CURLcode myssh_multi_statemach(struct connectdata *conn, + bool *done); +static CURLcode myssh_do_it(struct connectdata *conn, bool *done); + +static CURLcode scp_done(struct connectdata *conn, + CURLcode, bool premature); +static CURLcode scp_doing(struct connectdata *conn, bool *dophase_done); +static CURLcode scp_disconnect(struct connectdata *conn, + bool dead_connection); + +static CURLcode sftp_done(struct connectdata *conn, + CURLcode, bool premature); +static CURLcode sftp_doing(struct connectdata *conn, + bool *dophase_done); +static CURLcode sftp_disconnect(struct connectdata *conn, bool dead); +static +CURLcode sftp_perform(struct connectdata *conn, + bool *connected, + bool *dophase_done); + +static void sftp_quote(struct connectdata *conn); +static void sftp_quote_stat(struct connectdata *conn); + +static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock, + int numsocks); + +static int myssh_perform_getsock(const struct connectdata *conn, + curl_socket_t *sock, + int numsocks); + +static CURLcode myssh_setup_connection(struct connectdata *conn); + +/* + * SCP protocol handler. + */ + +const struct Curl_handler Curl_handler_scp = { + "SCP", /* scheme */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + scp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + scp_doing, /* doing */ + myssh_getsock, /* proto_getsock */ + myssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + myssh_perform_getsock, /* perform_getsock */ + scp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + PORT_SSH, /* defport */ + CURLPROTO_SCP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ +}; + +/* + * SFTP protocol handler. + */ + +const struct Curl_handler Curl_handler_sftp = { + "SFTP", /* scheme */ + myssh_setup_connection, /* setup_connection */ + myssh_do_it, /* do_it */ + sftp_done, /* done */ + ZERO_NULL, /* do_more */ + myssh_connect, /* connect_it */ + myssh_multi_statemach, /* connecting */ + sftp_doing, /* doing */ + myssh_getsock, /* proto_getsock */ + myssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ + myssh_perform_getsock, /* perform_getsock */ + sftp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ + PORT_SSH, /* defport */ + CURLPROTO_SFTP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ +}; + +static CURLcode sftp_error_to_CURLE(int err) +{ + switch(err) { + case SSH_FX_OK: + return CURLE_OK; + + case SSH_FX_NO_SUCH_FILE: + case SSH_FX_NO_SUCH_PATH: + return CURLE_REMOTE_FILE_NOT_FOUND; + + case SSH_FX_PERMISSION_DENIED: + case SSH_FX_WRITE_PROTECT: + return CURLE_REMOTE_ACCESS_DENIED; + + case SSH_FX_FILE_ALREADY_EXISTS: + return CURLE_REMOTE_FILE_EXISTS; + + default: + break; + } + + return CURLE_SSH; +} + +/* + * SSH State machine related code + */ +/* This is the ONLY way to change SSH state! */ +static void state(struct connectdata *conn, sshstate nowstate) +{ + struct ssh_conn *sshc = &conn->proto.sshc; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char *const names[] = { + "SSH_STOP", + "SSH_INIT", + "SSH_S_STARTUP", + "SSH_HOSTKEY", + "SSH_AUTHLIST", + "SSH_AUTH_PKEY_INIT", + "SSH_AUTH_PKEY", + "SSH_AUTH_PASS_INIT", + "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", + "SSH_AUTH_HOST_INIT", + "SSH_AUTH_HOST", + "SSH_AUTH_KEY_INIT", + "SSH_AUTH_KEY", + "SSH_AUTH_GSSAPI", + "SSH_AUTH_DONE", + "SSH_SFTP_INIT", + "SSH_SFTP_REALPATH", + "SSH_SFTP_QUOTE_INIT", + "SSH_SFTP_POSTQUOTE_INIT", + "SSH_SFTP_QUOTE", + "SSH_SFTP_NEXT_QUOTE", + "SSH_SFTP_QUOTE_STAT", + "SSH_SFTP_QUOTE_SETSTAT", + "SSH_SFTP_QUOTE_SYMLINK", + "SSH_SFTP_QUOTE_MKDIR", + "SSH_SFTP_QUOTE_RENAME", + "SSH_SFTP_QUOTE_RMDIR", + "SSH_SFTP_QUOTE_UNLINK", + "SSH_SFTP_QUOTE_STATVFS", + "SSH_SFTP_GETINFO", + "SSH_SFTP_FILETIME", + "SSH_SFTP_TRANS_INIT", + "SSH_SFTP_UPLOAD_INIT", + "SSH_SFTP_CREATE_DIRS_INIT", + "SSH_SFTP_CREATE_DIRS", + "SSH_SFTP_CREATE_DIRS_MKDIR", + "SSH_SFTP_READDIR_INIT", + "SSH_SFTP_READDIR", + "SSH_SFTP_READDIR_LINK", + "SSH_SFTP_READDIR_BOTTOM", + "SSH_SFTP_READDIR_DONE", + "SSH_SFTP_DOWNLOAD_INIT", + "SSH_SFTP_DOWNLOAD_STAT", + "SSH_SFTP_CLOSE", + "SSH_SFTP_SHUTDOWN", + "SSH_SCP_TRANS_INIT", + "SSH_SCP_UPLOAD_INIT", + "SSH_SCP_DOWNLOAD_INIT", + "SSH_SCP_DOWNLOAD", + "SSH_SCP_DONE", + "SSH_SCP_SEND_EOF", + "SSH_SCP_WAIT_EOF", + "SSH_SCP_WAIT_CLOSE", + "SSH_SCP_CHANNEL_FREE", + "SSH_SESSION_DISCONNECT", + "SSH_SESSION_FREE", + "QUIT" + }; + + + if(sshc->state != nowstate) { + infof(conn->data, "SSH %p state change from %s to %s\n", + (void *) sshc, names[sshc->state], names[nowstate]); + } +#endif + + sshc->state = nowstate; +} + +/* Multiple options: + * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 + * hash (90s style auth, not sure we should have it here) + * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first + * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE + * is returned by it. + * 3. none of the above. We only accept if it is present on known hosts. + * + * Returns SSH_OK or SSH_ERROR. + */ +static int myssh_is_known(struct connectdata *conn) +{ + int rc; + struct Curl_easy *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; + ssh_key pubkey; + size_t hlen; + unsigned char *hash = NULL; + char *base64 = NULL; + int vstate; + enum curl_khmatch keymatch; + struct curl_khkey foundkey; + curl_sshkeycallback func = + data->set.ssh_keyfunc; + + rc = ssh_get_publickey(sshc->ssh_session, &pubkey); + if(rc != SSH_OK) + return rc; + + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, + &hash, &hlen); + if(rc != SSH_OK) + goto cleanup; + + if(hlen != strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) || + memcmp(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], hash, hlen)) { + rc = SSH_ERROR; + goto cleanup; + } + + rc = SSH_OK; + goto cleanup; + } + + if(data->set.ssl.primary.verifyhost != TRUE) { + rc = SSH_OK; + goto cleanup; + } + + vstate = ssh_is_server_known(sshc->ssh_session); + switch(vstate) { + case SSH_SERVER_KNOWN_OK: + keymatch = CURLKHMATCH_OK; + break; + case SSH_SERVER_FILE_NOT_FOUND: + /* fallthrough */ + case SSH_SERVER_NOT_KNOWN: + keymatch = CURLKHMATCH_MISSING; + break; + default: + keymatch = CURLKHMATCH_MISMATCH; + break; + } + + if(func) { /* use callback to determine action */ + rc = ssh_pki_export_pubkey_base64(pubkey, &base64); + if(rc != SSH_OK) + goto cleanup; + + foundkey.key = base64; + foundkey.len = strlen(base64); + + switch(ssh_key_type(pubkey)) { + case SSH_KEYTYPE_RSA: + foundkey.keytype = CURLKHTYPE_RSA; + break; + case SSH_KEYTYPE_RSA1: + foundkey.keytype = CURLKHTYPE_RSA1; + break; + case SSH_KEYTYPE_ECDSA: + foundkey.keytype = CURLKHTYPE_ECDSA; + break; +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0) + case SSH_KEYTYPE_ED25519: + foundkey.keytype = CURLKHTYPE_ED25519; + break; +#endif + case SSH_KEYTYPE_DSS: + foundkey.keytype = CURLKHTYPE_DSS; + break; + default: + rc = SSH_ERROR; + goto cleanup; + } + + /* we don't have anything equivalent to knownkey. Always NULL */ + rc = func(data, NULL, &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + + switch(rc) { + case CURLKHSTAT_FINE_ADD_TO_FILE: + rc = ssh_write_knownhost(sshc->ssh_session); + if(rc != SSH_OK) { + goto cleanup; + } + break; + case CURLKHSTAT_FINE: + break; + default: /* REJECT/DEFER */ + rc = SSH_ERROR; + goto cleanup; + } + } + else { + if(keymatch != CURLKHMATCH_OK) { + rc = SSH_ERROR; + goto cleanup; + } + } + rc = SSH_OK; + +cleanup: + if(hash) + ssh_clean_pubkey_hash(&hash); + ssh_key_free(pubkey); + return rc; +} + +#define MOVE_TO_ERROR_STATE(_r) { \ + state(conn, SSH_SESSION_FREE); \ + sshc->actualcode = _r; \ + rc = SSH_ERROR; \ + break; \ +} + +#define MOVE_TO_SFTP_CLOSE_STATE() { \ + state(conn, SSH_SFTP_CLOSE); \ + sshc->actualcode = sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \ + rc = SSH_ERROR; \ + break; \ +} + +#define MOVE_TO_LAST_AUTH \ + if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ + rc = SSH_OK; \ + state(conn, SSH_AUTH_PASS_INIT); \ + break; \ + } \ + else { \ + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \ + } + +#define MOVE_TO_TERTIARY_AUTH \ + if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ + rc = SSH_OK; \ + state(conn, SSH_AUTH_KEY_INIT); \ + break; \ + } \ + else { \ + MOVE_TO_LAST_AUTH; \ + } + +#define MOVE_TO_SECONDARY_AUTH \ + if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ + rc = SSH_OK; \ + state(conn, SSH_AUTH_GSSAPI); \ + break; \ + } \ + else { \ + MOVE_TO_TERTIARY_AUTH; \ + } + +static +int myssh_auth_interactive(struct connectdata *conn) +{ + int rc; + struct ssh_conn *sshc = &conn->proto.sshc; + int nprompts; + +restart: + switch(sshc->kbd_state) { + case 0: + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + + if(rc != SSH_AUTH_INFO) + return SSH_ERROR; + + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts == SSH_ERROR || nprompts != 1) + return SSH_ERROR; + + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + if(rc < 0) + return SSH_ERROR; + + /* fallthrough */ + case 1: + sshc->kbd_state = 1; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else if(rc == SSH_AUTH_INFO) { + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts != 0) + return SSH_ERROR; + + sshc->kbd_state = 2; + goto restart; + } + else + rc = SSH_ERROR; + break; + case 2: + sshc->kbd_state = 2; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else + rc = SSH_ERROR; + + break; + default: + return SSH_ERROR; + } + + sshc->kbd_state = 0; + return rc; +} + +/* + * ssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the libssh function returns SSH_AGAIN + * meaning it wants to be called again when the socket is ready + */ +static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block) +{ + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + struct SSHPROTO *protop = data->req.protop; + struct ssh_conn *sshc = &conn->proto.sshc; + int rc = SSH_NO_ERROR, err; + char *new_readdir_line; + int seekerr = CURL_SEEKFUNC_OK; + const char *err_msg; + *block = 0; /* we're not blocking by default */ + + do { + + switch(sshc->state) { + case SSH_INIT: + sshc->secondCreateDirs = 0; + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_OK; + +#if 0 + ssh_set_log_level(SSH_LOG_PROTOCOL); +#endif + + /* Set libssh to non-blocking, since everything internally is + non-blocking */ + ssh_set_blocking(sshc->ssh_session, 0); + + state(conn, SSH_S_STARTUP); + /* fall-through */ + + case SSH_S_STARTUP: + rc = ssh_connect(sshc->ssh_session); + if(rc == SSH_AGAIN) + break; + + if(rc != SSH_OK) { + failf(data, "Failure establishing ssh session"); + MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT); + } + + state(conn, SSH_HOSTKEY); + + /* fall-through */ + case SSH_HOSTKEY: + + rc = myssh_is_known(conn); + if(rc != SSH_OK) { + MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION); + } + + state(conn, SSH_AUTHLIST); + /* fall through */ + case SSH_AUTHLIST:{ + sshc->authed = FALSE; + + rc = ssh_userauth_none(sshc->ssh_session, NULL); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Authenticated with none\n"); + state(conn, SSH_AUTH_DONE); + break; + } + else if(rc == SSH_AUTH_ERROR) { + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + } + + sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL); + if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + state(conn, SSH_AUTH_PKEY_INIT); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { + state(conn, SSH_AUTH_GSSAPI); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { + state(conn, SSH_AUTH_KEY_INIT); + } + else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { + state(conn, SSH_AUTH_PASS_INIT); + } + else { /* unsupported authentication method */ + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + } + + break; + } + case SSH_AUTH_PKEY_INIT: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { + MOVE_TO_SECONDARY_AUTH; + } + + /* Two choices, (1) private key was given on CMD, + * (2) use the "default" keys. */ + if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + if(sshc->pubkey && !data->set.ssl.key_passwd) { + rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, + sshc->pubkey); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc != SSH_OK) { + MOVE_TO_SECONDARY_AUTH; + } + } + + rc = ssh_pki_import_privkey_file(data-> + set.str[STRING_SSH_PRIVATE_KEY], + data->set.ssl.key_passwd, NULL, + NULL, &sshc->privkey); + if(rc != SSH_OK) { + failf(data, "Could not load private key file %s", + data->set.str[STRING_SSH_PRIVATE_KEY]); + break; + } + + state(conn, SSH_AUTH_PKEY); + break; + + } + else { + infof(data, "Authentication using SSH public key file\n"); + + rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, + data->set.ssl.key_passwd); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + if(rc == SSH_AUTH_SUCCESS) { + rc = SSH_OK; + sshc->authed = TRUE; + infof(data, "Completed public key authentication\n"); + state(conn, SSH_AUTH_DONE); + break; + } + + MOVE_TO_SECONDARY_AUTH; + } + break; + case SSH_AUTH_PKEY: + rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed public key authentication\n"); + state(conn, SSH_AUTH_DONE); + break; + } + else { + infof(data, "Failed public key authentication (rc: %d)\n", rc); + MOVE_TO_SECONDARY_AUTH; + } + break; + + case SSH_AUTH_GSSAPI: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { + MOVE_TO_TERTIARY_AUTH; + } + + rc = ssh_userauth_gssapi(sshc->ssh_session); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + rc = SSH_OK; + sshc->authed = TRUE; + infof(data, "Completed gssapi authentication\n"); + state(conn, SSH_AUTH_DONE); + break; + } + + MOVE_TO_TERTIARY_AUTH; + break; + + case SSH_AUTH_KEY_INIT: + if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { + state(conn, SSH_AUTH_KEY); + } + else { + MOVE_TO_LAST_AUTH; + } + break; + + case SSH_AUTH_KEY: + + /* Authentication failed. Continue with keyboard-interactive now. */ + rc = myssh_auth_interactive(conn); + if(rc == SSH_AGAIN) { + break; + } + if(rc == SSH_OK) { + sshc->authed = TRUE; + infof(data, "completed keyboard interactive authentication\n"); + } + state(conn, SSH_AUTH_DONE); + break; + + case SSH_AUTH_PASS_INIT: + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { + /* Host key authentication is intentionally not implemented */ + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + } + state(conn, SSH_AUTH_PASS); + /* fall through */ + + case SSH_AUTH_PASS: + rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd); + if(rc == SSH_AUTH_AGAIN) { + rc = SSH_AGAIN; + break; + } + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed password authentication\n"); + state(conn, SSH_AUTH_DONE); + } + else { + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + } + break; + + case SSH_AUTH_DONE: + if(!sshc->authed) { + failf(data, "Authentication failure"); + MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + break; + } + + /* + * At this point we have an authenticated ssh session. + */ + infof(data, "Authentication complete\n"); + + Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */ + + conn->sockfd = ssh_get_fd(sshc->ssh_session); + conn->writesockfd = CURL_SOCKET_BAD; + + if(conn->handler->protocol == CURLPROTO_SFTP) { + state(conn, SSH_SFTP_INIT); + break; + } + infof(data, "SSH CONNECT phase done\n"); + state(conn, SSH_STOP); + break; + + case SSH_SFTP_INIT: + ssh_set_blocking(sshc->ssh_session, 1); + + sshc->sftp_session = sftp_new(sshc->ssh_session); + if(!sshc->sftp_session) { + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + break; + } + + rc = sftp_init(sshc->sftp_session); + if(rc != SSH_OK) { + rc = sftp_get_error(sshc->sftp_session); + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(rc)); + break; + } + state(conn, SSH_SFTP_REALPATH); + /* fall through */ + case SSH_SFTP_REALPATH: + /* + * Get the "home" directory + */ + sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); + if(sshc->homedir == NULL) { + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + } + conn->data->state.most_recent_ftp_entrypath = sshc->homedir; + + /* This is the last step in the SFTP connect phase. Do note that while + we get the homedir here, we get the "workingpath" in the DO action + since the homedir will remain the same between request but the + working path will not. */ + DEBUGF(infof(data, "SSH CONNECT phase done\n")); + state(conn, SSH_STOP); + break; + + case SSH_SFTP_QUOTE_INIT: + + result = Curl_getworkingpath(conn, sshc->homedir, &protop->path); + if(result) { + sshc->actualcode = result; + state(conn, SSH_STOP); + break; + } + + if(data->set.quote) { + infof(data, "Sending quote commands\n"); + sshc->quote_item = data->set.quote; + state(conn, SSH_SFTP_QUOTE); + } + else { + state(conn, SSH_SFTP_GETINFO); + } + break; + + case SSH_SFTP_POSTQUOTE_INIT: + if(data->set.postquote) { + infof(data, "Sending quote commands\n"); + sshc->quote_item = data->set.postquote; + state(conn, SSH_SFTP_QUOTE); + } + else { + state(conn, SSH_STOP); + } + break; + + case SSH_SFTP_QUOTE: + /* Send any quote commands */ + sftp_quote(conn); + break; + + case SSH_SFTP_NEXT_QUOTE: + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + sshc->quote_item = sshc->quote_item->next; + + if(sshc->quote_item) { + state(conn, SSH_SFTP_QUOTE); + } + else { + if(sshc->nextstate != SSH_NO_STATE) { + state(conn, sshc->nextstate); + sshc->nextstate = SSH_NO_STATE; + } + else { + state(conn, SSH_SFTP_GETINFO); + } + } + break; + + case SSH_SFTP_QUOTE_STAT: + sftp_quote_stat(conn); + break; + + case SSH_SFTP_QUOTE_SETSTAT: + rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, + sshc->quote_attrs); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to set SFTP stats failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + /* sshc->actualcode = sftp_error_to_CURLE(err); + * we do not send the actual error; we return + * the error the libssh2 backend is returning */ + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_SYMLINK: + rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, + sshc->quote_path1); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "symlink command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_MKDIR: + rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, + (mode_t)data->set.new_directory_perms); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "mkdir command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RENAME: + rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, + sshc->quote_path2); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "rename command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_RMDIR: + rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "rmdir command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_UNLINK: + rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); + if(rc != 0 && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "rm command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + case SSH_SFTP_QUOTE_STATVFS: + { + sftp_statvfs_t statvfs; + + statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); + if(!statvfs && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "statvfs command failed: %s", + ssh_get_error(sshc->ssh_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } + else if(statvfs) { + char *tmp = aprintf("statvfs:\n" + "f_bsize: %llu\n" "f_frsize: %llu\n" + "f_blocks: %llu\n" "f_bfree: %llu\n" + "f_bavail: %llu\n" "f_files: %llu\n" + "f_ffree: %llu\n" "f_favail: %llu\n" + "f_fsid: %llu\n" "f_flag: %llu\n" + "f_namemax: %llu\n", + statvfs->f_bsize, statvfs->f_frsize, + statvfs->f_blocks, statvfs->f_bfree, + statvfs->f_bavail, statvfs->f_files, + statvfs->f_ffree, statvfs->f_favail, + statvfs->f_fsid, statvfs->f_flag, + statvfs->f_namemax); + sftp_statvfs_free(statvfs); + + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + break; + } + + result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + } + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + } + + case SSH_SFTP_GETINFO: + if(data->set.get_filetime) { + state(conn, SSH_SFTP_FILETIME); + } + else { + state(conn, SSH_SFTP_TRANS_INIT); + } + break; + + case SSH_SFTP_FILETIME: + { + sftp_attributes attrs; + + attrs = sftp_stat(sshc->sftp_session, protop->path); + if(attrs != 0) { + data->info.filetime = (long)attrs->mtime; + sftp_attributes_free(attrs); + } + + state(conn, SSH_SFTP_TRANS_INIT); + break; + } + + case SSH_SFTP_TRANS_INIT: + if(data->set.upload) + state(conn, SSH_SFTP_UPLOAD_INIT); + else { + if(protop->path[strlen(protop->path)-1] == '/') + state(conn, SSH_SFTP_READDIR_INIT); + else + state(conn, SSH_SFTP_DOWNLOAD_INIT); + } + break; + + case SSH_SFTP_UPLOAD_INIT: + { + int flags; + + if(data->state.resume_from != 0) { + sftp_attributes attrs; + + if(data->state.resume_from < 0) { + attrs = sftp_stat(sshc->sftp_session, protop->path); + if(attrs != 0) { + curl_off_t size = attrs->size; + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + } + data->state.resume_from = attrs->size; + + sftp_attributes_free(attrs); + } + else { + data->state.resume_from = 0; + } + } + } + + if(data->set.ftp_append) + /* Try to open for append, but create if nonexisting */ + flags = O_WRONLY|O_CREAT|O_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = O_WRONLY|O_APPEND; + else + /* Clear file before writing (normal behaviour) */ + flags = O_WRONLY|O_APPEND|O_CREAT|O_TRUNC; + + if(sshc->sftp_file) + sftp_close(sshc->sftp_file); + sshc->sftp_file = + sftp_open(sshc->sftp_session, protop->path, + flags, (mode_t)data->set.new_file_perms); + if(!sshc->sftp_file) { + err = sftp_get_error(sshc->sftp_session); + + if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || + err == SSH_FX_NO_SUCH_PATH)) && + (data->set.ftp_create_missing_dirs && + (strlen(protop->path) > 1))) { + /* try to create the path remotely */ + rc = 0; + sshc->secondCreateDirs = 1; + state(conn, SSH_SFTP_CREATE_DIRS_INIT); + break; + } + else { + MOVE_TO_SFTP_CLOSE_STATE(); + } + } + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + if(conn->seek_func) { + seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, + SEEK_SET); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + do { + size_t readthisamountnow = + (data->state.resume_from - passed > data->set.buffer_size) ? + (size_t)data->set.buffer_size : + curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread = + data->state.fread_func(data->state.buffer, 1, + readthisamountnow, data->state.in); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); + if(rc != 0) { + MOVE_TO_SFTP_CLOSE_STATE(); + } + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh sftp send function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_OUT; + + /* since we don't really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(conn, SSH_STOP); + break; + } + + case SSH_SFTP_CREATE_DIRS_INIT: + if(strlen(protop->path) > 1) { + sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */ + state(conn, SSH_SFTP_CREATE_DIRS); + } + else { + state(conn, SSH_SFTP_UPLOAD_INIT); + } + break; + + case SSH_SFTP_CREATE_DIRS: + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'\n", protop->path); + state(conn, SSH_SFTP_CREATE_DIRS_MKDIR); + break; + } + state(conn, SSH_SFTP_UPLOAD_INIT); + break; + + case SSH_SFTP_CREATE_DIRS_MKDIR: + /* 'mode' - parameter is preliminary - default to 0644 */ + rc = sftp_mkdir(sshc->sftp_session, protop->path, + (mode_t)data->set.new_directory_perms); + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure wasn't that the dir already exists or the + * permission was denied (creation might succeed further down the + * path) - retry on unspecific FAILURE also + */ + err = sftp_get_error(sshc->sftp_session); + if((err != SSH_FX_FILE_ALREADY_EXISTS) && + (err != SSH_FX_FAILURE) && + (err != SSH_FX_PERMISSION_DENIED)) { + MOVE_TO_SFTP_CLOSE_STATE(); + } + rc = 0; /* clear rc and continue */ + } + state(conn, SSH_SFTP_CREATE_DIRS); + break; + + case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->set.opt_no_body) { + state(conn, SSH_STOP); + break; + } + + /* + * This is a directory that we are trying to get, so produce a directory + * listing + */ + sshc->sftp_dir = sftp_opendir(sshc->sftp_session, + protop->path); + if(!sshc->sftp_dir) { + failf(data, "Could not open directory for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + } + state(conn, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR: + + if(sshc->readdir_attrs) + sftp_attributes_free(sshc->readdir_attrs); + + sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); + if(sshc->readdir_attrs) { + sshc->readdir_filename = sshc->readdir_attrs->name; + sshc->readdir_longentry = sshc->readdir_attrs->longname; + sshc->readdir_len = (int)strlen(sshc->readdir_filename); + + if(data->set.ftp_list_only) { + char *tmpLine; + + tmpLine = aprintf("%s\n", sshc->readdir_filename); + if(tmpLine == NULL) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + result = Curl_client_write(conn, CLIENTWRITE_BODY, + tmpLine, sshc->readdir_len + 1); + free(tmpLine); + + if(result) { + state(conn, SSH_STOP); + break; + } + /* since this counts what we send to the client, we include the + newline in this counter */ + data->req.bytecount += sshc->readdir_len + 1; + + /* output debug output if that is requested */ + if(data->set.verbose) { + Curl_debug(data, CURLINFO_DATA_OUT, + (char *)sshc->readdir_filename, + sshc->readdir_len, conn); + } + } + else { + sshc->readdir_currLen = (int)strlen(sshc->readdir_longentry); + sshc->readdir_totalLen = 80 + sshc->readdir_currLen; + sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); + if(!sshc->readdir_line) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + + memcpy(sshc->readdir_line, sshc->readdir_longentry, + sshc->readdir_currLen); + if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && + ((sshc->readdir_attrs->permissions & S_IFMT) == + S_IFLNK)) { + sshc->readdir_linkPath = malloc(PATH_MAX + 1); + if(sshc->readdir_linkPath == NULL) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + + snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", protop->path, + sshc->readdir_filename); + + state(conn, SSH_SFTP_READDIR_LINK); + break; + } + state(conn, SSH_SFTP_READDIR_BOTTOM); + break; + } + } + else if(sshc->readdir_attrs == NULL && sftp_dir_eof(sshc->sftp_dir)) { + state(conn, SSH_SFTP_READDIR_DONE); + break; + } + else { + failf(data, "Could not open remote file for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + break; + } + break; + + case SSH_SFTP_READDIR_LINK: + if(sshc->readdir_link_attrs) + sftp_attributes_free(sshc->readdir_link_attrs); + + sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, + sshc->readdir_linkPath); + if(sshc->readdir_link_attrs == 0) { + failf(data, "Could not read symlink for reading: %s", + ssh_get_error(sshc->ssh_session)); + MOVE_TO_SFTP_CLOSE_STATE(); + } + + if(sshc->readdir_link_attrs->name == NULL) { + sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, + sshc->readdir_linkPath); + if(sshc->readdir_filename == NULL) + sshc->readdir_len = 0; + else + sshc->readdir_len = (int)strlen(sshc->readdir_tmp); + sshc->readdir_longentry = NULL; + sshc->readdir_filename = sshc->readdir_tmp; + } + else { + sshc->readdir_len = (int)strlen(sshc->readdir_link_attrs->name); + sshc->readdir_filename = sshc->readdir_link_attrs->name; + sshc->readdir_longentry = sshc->readdir_link_attrs->longname; + } + + Curl_safefree(sshc->readdir_linkPath); + + /* get room for the filename and extra output */ + sshc->readdir_totalLen += 4 + sshc->readdir_len; + new_readdir_line = Curl_saferealloc(sshc->readdir_line, + sshc->readdir_totalLen); + if(!new_readdir_line) { + sshc->readdir_line = NULL; + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + sshc->readdir_line = new_readdir_line; + + sshc->readdir_currLen += snprintf(sshc->readdir_line + + sshc->readdir_currLen, + sshc->readdir_totalLen - + sshc->readdir_currLen, + " -> %s", + sshc->readdir_filename); + + sftp_attributes_free(sshc->readdir_link_attrs); + sshc->readdir_link_attrs = NULL; + sshc->readdir_filename = NULL; + sshc->readdir_longentry = NULL; + + state(conn, SSH_SFTP_READDIR_BOTTOM); + /* fall through */ + case SSH_SFTP_READDIR_BOTTOM: + sshc->readdir_currLen += snprintf(sshc->readdir_line + + sshc->readdir_currLen, + sshc->readdir_totalLen - + sshc->readdir_currLen, "\n"); + result = Curl_client_write(conn, CLIENTWRITE_BODY, + sshc->readdir_line, + sshc->readdir_currLen); + + if(!result) { + + /* output debug output if that is requested */ + if(data->set.verbose) { + Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, + sshc->readdir_currLen, conn); + } + data->req.bytecount += sshc->readdir_currLen; + } + Curl_safefree(sshc->readdir_line); + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + + if(result) { + state(conn, SSH_STOP); + } + else + state(conn, SSH_SFTP_READDIR); + break; + + case SSH_SFTP_READDIR_DONE: + sftp_closedir(sshc->sftp_dir); + sshc->sftp_dir = NULL; + + /* no data to transfer */ + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + state(conn, SSH_STOP); + break; + + case SSH_SFTP_DOWNLOAD_INIT: + /* + * Work on getting the specified file + */ + if(sshc->sftp_file) + sftp_close(sshc->sftp_file); + + sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path, + O_RDONLY, (mode_t)data->set.new_file_perms); + if(!sshc->sftp_file) { + failf(data, "Could not open remote file for reading: %s", + ssh_get_error(sshc->ssh_session)); + + MOVE_TO_SFTP_CLOSE_STATE(); + } + + state(conn, SSH_SFTP_DOWNLOAD_STAT); + break; + + case SSH_SFTP_DOWNLOAD_STAT: + { + sftp_attributes attrs; + curl_off_t size; + + attrs = sftp_fstat(sshc->sftp_file); + if(!attrs || + !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) || + (attrs->size == 0)) { + /* + * sftp_fstat didn't return an error, so maybe the server + * just doesn't support stat() + * OR the server doesn't return a file size with a stat() + * OR file size is 0 + */ + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + size = 0; + } + else { + size = attrs->size; + + sftp_attributes_free(attrs); + + if(size < 0) { + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(conn->data->state.use_range) { + curl_off_t from, to; + char *ptr; + char *ptr2; + CURLofft to_t; + CURLofft from_t; + + from_t = curlx_strtoofft(conn->data->state.range, &ptr, 0, &from); + if(from_t == CURL_OFFT_FLOW) { + return CURLE_RANGE_ERROR; + } + while(*ptr && (ISSPACE(*ptr) || (*ptr == '-'))) + ptr++; + to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); + if(to_t == CURL_OFFT_FLOW) { + return CURLE_RANGE_ERROR; + } + if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ + || (to >= size)) { + to = size - 1; + } + if(from_t) { + /* from is relative to end of file */ + from = size - to; + to = size - 1; + } + if(from > size) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(from > to) { + from = to; + size = 0; + } + else { + size = to - from + 1; + } + + rc = sftp_seek64(sshc->sftp_file, from); + if(rc != 0) { + MOVE_TO_SFTP_CLOSE_STATE(); + } + } + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + } + + /* We can resume if we can seek to the resume position */ + if(data->state.resume_from) { + if(data->state.resume_from < 0) { + /* We're supposed to download the last abs(from) bytes */ + if((curl_off_t)size < -data->state.resume_from) { + failf(data, "Offset (%" + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* download from where? */ + data->state.resume_from += size; + } + else { + if((curl_off_t)size < data->state.resume_from) { + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + data->state.resume_from, size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + /* Does a completed file need to be seeked and started or closed ? */ + /* Now store the number of bytes we are expected to download */ + data->req.size = size - data->state.resume_from; + data->req.maxdownload = size - data->state.resume_from; + Curl_pgrsSetDownloadSize(data, + size - data->state.resume_from); + + rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); + if(rc != 0) { + MOVE_TO_SFTP_CLOSE_STATE(); + } + } + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + infof(data, "File already completely downloaded\n"); + state(conn, SSH_STOP); + break; + } + Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size, + FALSE, NULL, -1, NULL); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh recv function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_IN; + + if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + sshc->sftp_recv_state = 0; + state(conn, SSH_STOP); + } + break; + + case SSH_SFTP_CLOSE: + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + Curl_safefree(protop->path); + + DEBUGF(infof(data, "SFTP DONE done\n")); + + /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT + After nextstate is executed, the control should come back to + SSH_SFTP_CLOSE to pass the correct result back */ + if(sshc->nextstate != SSH_NO_STATE && + sshc->nextstate != SSH_SFTP_CLOSE) { + state(conn, sshc->nextstate); + sshc->nextstate = SSH_SFTP_CLOSE; + } + else { + state(conn, SSH_STOP); + result = sshc->actualcode; + } + break; + + case SSH_SFTP_SHUTDOWN: + /* during times we get here due to a broken transfer and then the + sftp_handle might not have been taken down so make sure that is done + before we proceed */ + + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + + Curl_safefree(sshc->homedir); + conn->data->state.most_recent_ftp_entrypath = NULL; + + state(conn, SSH_SESSION_DISCONNECT); + break; + + + case SSH_SCP_TRANS_INIT: + result = Curl_getworkingpath(conn, sshc->homedir, &protop->path); + if(result) { + sshc->actualcode = result; + state(conn, SSH_STOP); + break; + } + + /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ + ssh_set_blocking(sshc->ssh_session, 1); + + if(data->set.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + sshc->actualcode = CURLE_UPLOAD_FAILED; + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + } + + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path); + state(conn, SSH_SCP_UPLOAD_INIT); + } + else { + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path); + state(conn, SSH_SCP_DOWNLOAD_INIT); + } + + if(!sshc->scp_session) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(conn->data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + } + + break; + + case SSH_SCP_UPLOAD_INIT: + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(conn->data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + } + + rc = ssh_scp_push_file(sshc->scp_session, protop->path, + data->state.infilesize, + (int)data->set.new_file_perms); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(conn->data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + } + + /* upload data */ + Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL, + FIRSTSOCKET, NULL); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->sockfd = conn->writesockfd; + + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh scp send function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_OUT; + + state(conn, SSH_STOP); + + break; + + case SSH_SCP_DOWNLOAD_INIT: + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(conn->data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + } + state(conn, SSH_SCP_DOWNLOAD); + /* fall through */ + + case SSH_SCP_DOWNLOAD:{ + curl_off_t bytecount; + + rc = ssh_scp_pull_request(sshc->scp_session); + if(rc != SSH_SCP_REQUEST_NEWFILE) { + err_msg = ssh_get_error(sshc->ssh_session); + failf(conn->data, "%s", err_msg); + MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND); + break; + } + + /* download data */ + bytecount = ssh_scp_request_get_size(sshc->scp_session); + data->req.maxdownload = (curl_off_t) bytecount; + Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1, + NULL); + + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->writesockfd = conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh recv function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_IN; + + state(conn, SSH_STOP); + break; + } + case SSH_SCP_DONE: + if(data->set.upload) + state(conn, SSH_SCP_SEND_EOF); + else + state(conn, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_SEND_EOF: + if(sshc->scp_session) { + rc = ssh_scp_close(sshc->scp_session); + if(rc == SSH_AGAIN) { + /* Currently the ssh_scp_close handles waiting for EOF in + * blocking way. + */ + break; + } + if(rc != SSH_OK) { + infof(data, "Failed to close libssh scp channel: %s\n", + ssh_get_error(sshc->ssh_session)); + } + } + + state(conn, SSH_SCP_CHANNEL_FREE); + break; + + case SSH_SCP_CHANNEL_FREE: + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + DEBUGF(infof(data, "SCP DONE phase complete\n")); + + ssh_set_blocking(sshc->ssh_session, 0); + + state(conn, SSH_SESSION_DISCONNECT); + /* fall through */ + + case SSH_SESSION_DISCONNECT: + /* during weird times when we've been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + + ssh_disconnect(sshc->ssh_session); + + Curl_safefree(sshc->homedir); + conn->data->state.most_recent_ftp_entrypath = NULL; + + state(conn, SSH_SESSION_FREE); + /* fall through */ + case SSH_SESSION_FREE: + if(sshc->ssh_session) { + ssh_free(sshc->ssh_session); + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->scp_session == NULL); + + if(sshc->readdir_tmp) { + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + } + + if(sshc->quote_attrs) + sftp_attributes_free(sshc->quote_attrs); + + if(sshc->readdir_attrs) + sftp_attributes_free(sshc->readdir_attrs); + + if(sshc->readdir_link_attrs) + sftp_attributes_free(sshc->readdir_link_attrs); + + if(sshc->privkey) + ssh_key_free(sshc->privkey); + if(sshc->pubkey) + ssh_key_free(sshc->pubkey); + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + Curl_safefree(sshc->homedir); + + Curl_safefree(sshc->readdir_line); + Curl_safefree(sshc->readdir_linkPath); + + /* the code we are about to return */ + result = sshc->actualcode; + + memset(sshc, 0, sizeof(struct ssh_conn)); + + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + state(conn, SSH_STOP); + break; + + case SSH_QUIT: + /* fallthrough, just stop! */ + default: + /* internal error */ + sshc->nextstate = SSH_NO_STATE; + state(conn, SSH_STOP); + break; + + } + } while(!rc && (sshc->state != SSH_STOP)); + + + if(rc == SSH_AGAIN) { + /* we would block, we need to wait for the socket to be ready (in the + right direction too)! */ + *block = TRUE; + } + + return result; +} + + +/* called by the multi interface to figure out what socket(s) to wait for and + for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ +static int myssh_perform_getsock(const struct connectdata *conn, + curl_socket_t *sock, /* points to numsocks + number of sockets */ + int numsocks) +{ + int bitmap = GETSOCK_BLANK; + (void) numsocks; + + sock[0] = conn->sock[FIRSTSOCKET]; + + if(conn->waitfor & KEEP_RECV) + bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + + if(conn->waitfor & KEEP_SEND) + bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + + return bitmap; +} + +/* Generic function called by the multi interface to figure out what socket(s) + to wait for and for what actions during the DOING and PROTOCONNECT states*/ +static int myssh_getsock(struct connectdata *conn, + curl_socket_t *sock, /* points to numsocks + number of sockets */ + int numsocks) +{ + /* if we know the direction we can use the generic *_getsock() function even + for the protocol_connect and doing states */ + return myssh_perform_getsock(conn, sock, numsocks); +} + +static void myssh_block2waitfor(struct connectdata *conn, bool block) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + int dir; + + /* If it didn't block, or nothing was returned by ssh_get_poll_flags + * have the original set */ + conn->waitfor = sshc->orig_waitfor; + + if(block) { + dir = ssh_get_poll_flags(sshc->ssh_session); + if(dir & SSH_READ_PENDING) { + /* translate the libssh define bits into our own bit defines */ + conn->waitfor = KEEP_RECV; + } + else if(dir & SSH_WRITE_PENDING) { + conn->waitfor = KEEP_SEND; + } + } +} + +/* called repeatedly until done from multi.c */ +static CURLcode myssh_multi_statemach(struct connectdata *conn, + bool *done) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + bool block; /* we store the status and use that to provide a ssh_getsock() + implementation */ + + result = myssh_statemach_act(conn, &block); + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + myssh_block2waitfor(conn, block); + + return result; +} + +static CURLcode myssh_block_statemach(struct connectdata *conn, + bool disconnect) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; + + while((sshc->state != SSH_STOP) && !result) { + bool block; + timediff_t left = 1000; + struct curltime now = Curl_now(); + + result = myssh_statemach_act(conn, &block); + if(result) + break; + + if(!disconnect) { + if(Curl_pgrsUpdate(conn)) + return CURLE_ABORTED_BY_CALLBACK; + + result = Curl_speedcheck(data, now); + if(result) + break; + + left = Curl_timeleft(data, NULL, FALSE); + if(left < 0) { + failf(data, "Operation timed out"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + if(!result && block) { + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + curl_socket_t fd_read = CURL_SOCKET_BAD; + fd_read = sock; + /* wait for the socket to become ready */ + (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, left > 1000 ? 1000 : left); + } + + } + + return result; +} + +/* + * SSH setup connection + */ +static CURLcode myssh_setup_connection(struct connectdata *conn) +{ + struct SSHPROTO *ssh; + + conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO)); + if(!ssh) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static Curl_recv scp_recv, sftp_recv; +static Curl_send scp_send, sftp_send; + +/* + * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to + * do protocol-specific actions at connect-time. + */ +static CURLcode myssh_connect(struct connectdata *conn, bool *done) +{ + struct ssh_conn *ssh; + CURLcode result; + struct Curl_easy *data = conn->data; + int rc; + + /* initialize per-handle data if not already */ + if(!data->req.protop) + myssh_setup_connection(conn); + + /* We default to persistent connections. We set this already in this connect + function to make the re-use checks properly be able to check this bit. */ + connkeep(conn, "SSH default"); + + if(conn->handler->protocol & CURLPROTO_SCP) { + conn->recv[FIRSTSOCKET] = scp_recv; + conn->send[FIRSTSOCKET] = scp_send; + } + else { + conn->recv[FIRSTSOCKET] = sftp_recv; + conn->send[FIRSTSOCKET] = sftp_send; + } + + ssh = &conn->proto.sshc; + + ssh->ssh_session = ssh_new(); + if(ssh->ssh_session == NULL) { + failf(data, "Failure initialising ssh session"); + return CURLE_FAILED_INIT; + } + + if(conn->user) { + infof(data, "User: %s\n", conn->user); + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); + } + + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + infof(data, "Known hosts: %s\n", data->set.str[STRING_SSH_KNOWNHOSTS]); + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, + data->set.str[STRING_SSH_KNOWNHOSTS]); + } + + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + if(conn->remote_port) + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, + &conn->remote_port); + + if(data->set.ssh_compression) { + ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, + "zlib,zlib@openssh.com,none"); + } + + ssh->privkey = NULL; + ssh->pubkey = NULL; + + if(data->set.str[STRING_SSH_PUBLIC_KEY]) { + rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], + &ssh->pubkey); + if(rc != SSH_OK) { + failf(data, "Could not load public key file"); + /* ignore */ + } + } + + /* we do not verify here, we do it at the state machine, + * after connection */ + + state(conn, SSH_INIT); + + result = myssh_multi_statemach(conn, done); + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode scp_doing(struct connectdata *conn, bool *dophase_done) +{ + CURLcode result; + + result = myssh_multi_statemach(conn, dophase_done); + + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + return result; +} + +/* + *********************************************************************** + * + * scp_perform() + * + * This is the actual DO function for SCP. Get a file according to + * the options previously setup. + */ + +static +CURLcode scp_perform(struct connectdata *conn, + bool *connected, bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(conn->data, "DO phase starts\n")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(conn, SSH_SCP_TRANS_INIT); + + result = myssh_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; + + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + + return result; +} + +static CURLcode myssh_do_it(struct connectdata *conn, bool *done) +{ + CURLcode result; + bool connected = 0; + struct Curl_easy *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; + + *done = FALSE; /* default to false */ + + data->req.size = -1; /* make sure this is unknown at this point */ + + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs = 0; /* reset the create dir attempt state + variable */ + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + + if(conn->handler->protocol & CURLPROTO_SCP) + result = scp_perform(conn, &connected, done); + else + result = sftp_perform(conn, &connected, done); + + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode scp_disconnect(struct connectdata *conn, + bool dead_connection) +{ + CURLcode result = CURLE_OK; + struct ssh_conn *ssh = &conn->proto.sshc; + (void) dead_connection; + + if(ssh->ssh_session) { + /* only if there's a session still around to use! */ + + state(conn, SSH_SESSION_DISCONNECT); + + result = myssh_block_statemach(conn, TRUE); + } + + return result; +} + +/* generic done function for both SCP and SFTP called from their specific + done functions */ +static CURLcode myssh_done(struct connectdata *conn, CURLcode status) +{ + CURLcode result = CURLE_OK; + struct SSHPROTO *protop = conn->data->req.protop; + + if(!status) { + /* run the state-machine + + TODO: when the multi interface is used, this _really_ should be using + the ssh_multi_statemach function but we have no general support for + non-blocking DONE operations! + */ + result = myssh_block_statemach(conn, FALSE); + } + else + result = status; + + if(protop) + Curl_safefree(protop->path); + if(Curl_pgrsDone(conn)) + return CURLE_ABORTED_BY_CALLBACK; + + conn->data->req.keepon = 0; /* clear all bits */ + return result; +} + + +static CURLcode scp_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + (void) premature; /* not used */ + + if(!status) + state(conn, SSH_SCP_DONE); + + return myssh_done(conn, status); + +} + +static ssize_t scp_send(struct connectdata *conn, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + int rc; + (void) sockindex; /* we only support SCP on the fixed known primary socket */ + (void) err; + + rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len); + +#if 0 + /* The following code is misleading, mostly added as wishful thinking + * that libssh at some point will implement non-blocking ssh_scp_write/read. + * Currently rc can only be number of bytes read or SSH_ERROR. */ + myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); + + if(rc == SSH_AGAIN) { + *err = CURLE_AGAIN; + return 0; + } + else +#endif + if(rc != SSH_OK) { + *err = CURLE_SSH; + return -1; + } + + return len; +} + +static ssize_t scp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + (void) err; + (void) sockindex; /* we only support SCP on the fixed known primary socket */ + + /* libssh returns int */ + nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len); + +#if 0 + /* The following code is misleading, mostly added as wishful thinking + * that libssh at some point will implement non-blocking ssh_scp_write/read. + * Currently rc can only be SSH_OK or SSH_ERROR. */ + + myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); + if(nread == SSH_AGAIN) { + *err = CURLE_AGAIN; + nread = -1; + } +#endif + + return nread; +} + +/* + * =============== SFTP =============== + */ + +/* + *********************************************************************** + * + * sftp_perform() + * + * This is the actual DO function for SFTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode sftp_perform(struct connectdata *conn, + bool *connected, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + + DEBUGF(infof(conn->data, "DO phase starts\n")); + + *dophase_done = FALSE; /* not done yet */ + + /* start the first command in the DO phase */ + state(conn, SSH_SFTP_QUOTE_INIT); + + /* run the state-machine */ + result = myssh_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; + + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + + return result; +} + +/* called from multi.c while DOing */ +static CURLcode sftp_doing(struct connectdata *conn, + bool *dophase_done) +{ + CURLcode result = myssh_multi_statemach(conn, dophase_done); + if(*dophase_done) { + DEBUGF(infof(conn->data, "DO phase is complete\n")); + } + return result; +} + +/* BLOCKING, but the function is using the state machine so the only reason + this is still blocking is that the multi interface code has no support for + disconnecting operations that takes a while */ +static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection) +{ + CURLcode result = CURLE_OK; + (void) dead_connection; + + DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n")); + + if(conn->proto.sshc.ssh_session) { + /* only if there's a session still around to use! */ + state(conn, SSH_SFTP_SHUTDOWN); + result = myssh_block_statemach(conn, TRUE); + } + + DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n")); + + return result; + +} + +static CURLcode sftp_done(struct connectdata *conn, CURLcode status, + bool premature) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + + if(!status) { + /* Post quote commands are executed after the SFTP_CLOSE state to avoid + errors that could happen due to open file handles during POSTQUOTE + operation */ + if(!status && !premature && conn->data->set.postquote) { + sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; + state(conn, SSH_SFTP_CLOSE); + } + else + state(conn, SSH_SFTP_CLOSE); + } + return myssh_done(conn, status); +} + +/* return number of sent bytes */ +static ssize_t sftp_send(struct connectdata *conn, int sockindex, + const void *mem, size_t len, CURLcode *err) +{ + ssize_t nwrite; + (void)sockindex; + + nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len); + + myssh_block2waitfor(conn, FALSE); + +#if 0 /* not returned by libssh on write */ + if(nwrite == SSH_AGAIN) { + *err = CURLE_AGAIN; + nwrite = 0; + } + else +#endif + if(nwrite < 0) { + *err = CURLE_SSH; + nwrite = -1; + } + + return nwrite; +} + +/* + * Return number of received (decrypted) bytes + * or <0 on error + */ +static ssize_t sftp_recv(struct connectdata *conn, int sockindex, + char *mem, size_t len, CURLcode *err) +{ + ssize_t nread; + (void)sockindex; + + if(len >= (size_t)1<<32) + len = (size_t)(1<<31)-1; + + switch(conn->proto.sshc.sftp_recv_state) { + case 0: + conn->proto.sshc.sftp_file_index = + sftp_async_read_begin(conn->proto.sshc.sftp_file, + (uint32_t)len); + if(conn->proto.sshc.sftp_file_index < 0) { + *err = CURLE_RECV_ERROR; + return -1; + } + + /* fall-through */ + case 1: + conn->proto.sshc.sftp_recv_state = 1; + + nread = sftp_async_read(conn->proto.sshc.sftp_file, + mem, (uint32_t)len, + conn->proto.sshc.sftp_file_index); + + myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE); + + if(nread == SSH_AGAIN) { + *err = CURLE_AGAIN; + return -1; + } + else if(nread < 0) { + *err = CURLE_RECV_ERROR; + return -1; + } + + conn->proto.sshc.sftp_recv_state = 0; + return nread; + + default: + /* we never reach here */ + return -1; + } +} + +static void sftp_quote(struct connectdata *conn) +{ + const char *cp; + struct Curl_easy *data = conn->data; + struct SSHPROTO *protop = data->req.protop; + struct ssh_conn *sshc = &conn->proto.sshc; + CURLcode result; + + /* + * Support some of the "FTP" commands + */ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server reponds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(strcasecompare("pwd", cmd)) { + /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", + protop->path); + if(!tmp) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + return; + } + if(data->set.verbose) { + Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4, conn); + Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn); + } + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + else + state(conn, SSH_SFTP_NEXT_QUOTE); + return; + } + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(cp == NULL) { + failf(data, "Syntax error in SFTP command. Supply parameter(s)!"); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter"); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + + /* + * SFTP is a binary protocol, so we don't send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh + * functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in chgrp/chmod/chown: " + "Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + sshc->quote_attrs = NULL; + state(conn, SSH_SFTP_QUOTE_STAT); + return; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + state(conn, SSH_SFTP_QUOTE_SYMLINK); + return; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(conn, SSH_SFTP_QUOTE_MKDIR); + return; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return; + } + state(conn, SSH_SFTP_QUOTE_RENAME); + return; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(conn, SSH_SFTP_QUOTE_RMDIR); + return; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(conn, SSH_SFTP_QUOTE_UNLINK); + return; + } +#ifdef HAS_STATVFS_SUPPORT + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(conn, SSH_SFTP_QUOTE_STATVFS); + return; + } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; +} + +static void sftp_quote_stat(struct connectdata *conn) +{ + struct Curl_easy *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server reponds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + /* We read the file attributes, store them in sshc->quote_attrs + * and modify them accordingly to command. Then we switch to + * QUOTE_SETSTAT state to write new ones. + */ + + if(sshc->quote_attrs) + sftp_attributes_free(sshc->quote_attrs); + sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); + if(sshc->quote_attrs == NULL) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to get SFTP stats failed: %d", + sftp_get_error(sshc->sftp_session)); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + + /* Now set the new attributes... */ + if(strncasecompare(cmd, "chgrp", 5)) { + sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); + if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chgrp gid not a number"); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + else if(strncasecompare(cmd, "chmod", 5)) { + mode_t perms; + perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8); + /* permissions are octal */ + if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chmod permissions not a number"); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->permissions = perms; + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + } + else if(strncasecompare(cmd, "chown", 5)) { + sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); + if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chown uid not a number"); + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + + /* Now send the completed structure... */ + state(conn, SSH_SFTP_QUOTE_SETSTAT); + return; +} + + +#endif /* USE_LIBSSH */ diff --git a/Utilities/cmcurl/lib/ssh.c b/Utilities/cmcurl/lib/ssh.c index 5406bf0..a86ed70 100644 --- a/Utilities/cmcurl/lib/ssh.c +++ b/Utilities/cmcurl/lib/ssh.c @@ -26,9 +26,7 @@ #ifdef USE_LIBSSH2 -#ifdef HAVE_LIMITS_H -# include <limits.h> -#endif +#include <limits.h> #include <libssh2.h> #include <libssh2_sftp.h> @@ -87,21 +85,9 @@ /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" +#include "curl_path.h" #include "memdebug.h" -#ifdef WIN32 -# undef PATH_MAX -# define PATH_MAX MAX_PATH -# ifndef R_OK -# define R_OK 4 -# endif -#endif - -#ifndef PATH_MAX -#define PATH_MAX 1024 /* just an extra precaution since there are systems that - have their definition hidden well */ -#endif - #if LIBSSH2_VERSION_NUM >= 0x010206 /* libssh2_sftp_statvfs and friends were added in 1.2.6 */ #define HAS_STATVFS_SUPPORT 1 @@ -120,16 +106,10 @@ static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); static LIBSSH2_FREE_FUNC(my_libssh2_free); -static CURLcode get_pathname(const char **cpp, char **path); - static CURLcode ssh_connect(struct connectdata *conn, bool *done); static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done); static CURLcode ssh_do(struct connectdata *conn, bool *done); -static CURLcode ssh_getworkingpath(struct connectdata *conn, - char *homedir, /* when SFTP is used */ - char **path); - static CURLcode scp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode scp_doing(struct connectdata *conn, @@ -279,6 +259,11 @@ static CURLcode libssh2_session_error_to_CURLE(int err) case LIBSSH2_ERROR_NONE: return CURLE_OK; + /* This is the error returned by libssh2_scp_recv2 + * on unknown file */ + case LIBSSH2_ERROR_SCP_PROTOCOL: + return CURLE_REMOTE_FILE_NOT_FOUND; + case LIBSSH2_ERROR_SOCKET_NONE: return CURLE_COULDNT_CONNECT; @@ -410,70 +395,6 @@ static void state(struct connectdata *conn, sshstate nowstate) sshc->state = nowstate; } -/* figure out the path to work with in this particular request */ -static CURLcode ssh_getworkingpath(struct connectdata *conn, - char *homedir, /* when SFTP is used */ - char **path) /* returns the allocated - real path to work with */ -{ - struct Curl_easy *data = conn->data; - char *real_path = NULL; - char *working_path; - size_t working_path_len; - CURLcode result = - Curl_urldecode(data, data->state.path, 0, &working_path, - &working_path_len, FALSE); - if(result) - return result; - - /* Check for /~/, indicating relative to the user's home directory */ - if(conn->handler->protocol & CURLPROTO_SCP) { - real_path = malloc(working_path_len + 1); - if(real_path == NULL) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) - /* It is referenced to the home directory, so strip the leading '/~/' */ - memcpy(real_path, working_path + 3, 4 + working_path_len-3); - else - memcpy(real_path, working_path, 1 + working_path_len); - } - else if(conn->handler->protocol & CURLPROTO_SFTP) { - if((working_path_len > 1) && (working_path[1] == '~')) { - size_t homelen = strlen(homedir); - real_path = malloc(homelen + working_path_len + 1); - if(real_path == NULL) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - /* It is referenced to the home directory, so strip the - leading '/' */ - memcpy(real_path, homedir, homelen); - real_path[homelen] = '/'; - real_path[homelen + 1] = '\0'; - if(working_path_len > 3) { - memcpy(real_path + homelen + 1, working_path + 3, - 1 + working_path_len -3); - } - } - else { - real_path = malloc(working_path_len + 1); - if(real_path == NULL) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - memcpy(real_path, working_path, 1 + working_path_len); - } - } - - free(working_path); - - /* store the pointer for the caller to receive */ - *path = real_path; - - return CURLE_OK; -} #ifdef HAVE_LIBSSH2_KNOWNHOST_API static int sshkeycallback(struct Curl_easy *easy, @@ -1034,11 +955,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->sshagent_identity); if(rc < 0) { - if(rc != LIBSSH2_ERROR_EAGAIN) + if(rc != LIBSSH2_ERROR_EAGAIN) { /* tried and failed? go to next identity */ sshc->sshagent_prev_identity = sshc->sshagent_identity; - else - break; + } + break; } } @@ -1184,7 +1105,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_INIT: - result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); + result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path); if(result) { sshc->actualcode = result; state(conn, SSH_STOP); @@ -1219,6 +1140,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* * Support some of the "FTP" commands + * + * 'sshc->quote_item' is already verified to be non-NULL before it + * switched to this state. */ char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; @@ -1261,7 +1185,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) state(conn, SSH_SFTP_NEXT_QUOTE); break; } - if(cmd) { + { /* * the arguments following the command must be separated from the * command with a space so we can check for it unconditionally @@ -1279,7 +1203,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) * also, every command takes at least one argument so we get that * first argument right now */ - result = get_pathname(&cp, &sshc->quote_path1); + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); @@ -1304,7 +1228,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* sshc->quote_path1 contains the mode to set */ /* get the destination */ - result = get_pathname(&cp, &sshc->quote_path2); + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); @@ -1326,7 +1250,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* symbolic linking */ /* sshc->quote_path1 is the source */ /* get the destination */ - result = get_pathname(&cp, &sshc->quote_path2); + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); @@ -1351,7 +1275,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* rename file */ /* first param is the source path */ /* second param is the dest. path */ - result = get_pathname(&cp, &sshc->quote_path2); + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); if(result) { if(result == CURLE_OUT_OF_MEMORY) failf(data, "Out of memory"); @@ -1391,9 +1315,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } } - if(!sshc->quote_item) { - state(conn, SSH_SFTP_GETINFO); - } break; case SSH_SFTP_NEXT_QUOTE: @@ -2347,8 +2268,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } sshc->sftp_handle = NULL; } - if(sftp_scp) - Curl_safefree(sftp_scp->path); + + Curl_safefree(sftp_scp->path); DEBUGF(infof(data, "SFTP DONE done\n")); @@ -2399,7 +2320,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SCP_TRANS_INIT: - result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); + result = Curl_getworkingpath(conn, sshc->homedir, &sftp_scp->path); if(result) { sshc->actualcode = result; state(conn, SSH_STOP); @@ -2445,6 +2366,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) failf(conn->data, "%s", err_msg); state(conn, SSH_SCP_CHANNEL_FREE); sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); + /* Map generic errors to upload failed */ + if(sshc->actualcode == CURLE_SSH || + sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND) + sshc->actualcode = CURLE_UPLOAD_FAILED; break; } @@ -2833,8 +2758,8 @@ static CURLcode ssh_block_statemach(struct connectdata *conn, while((sshc->state != SSH_STOP) && !result) { bool block; - time_t left = 1000; - struct curltime now = Curl_tvnow(); + timediff_t left = 1000; + struct curltime now = Curl_now(); result = ssh_statemach_act(conn, &block); if(result) @@ -3307,93 +3232,6 @@ static ssize_t sftp_recv(struct connectdata *conn, int sockindex, return nread; } -/* The get_pathname() function is being borrowed from OpenSSH sftp.c - version 4.6p1. */ -/* - * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -static CURLcode -get_pathname(const char **cpp, char **path) -{ - const char *cp = *cpp, *end; - char quot; - unsigned int i, j; - static const char WHITESPACE[] = " \t\r\n"; - - cp += strspn(cp, WHITESPACE); - if(!*cp) { - *cpp = cp; - *path = NULL; - return CURLE_QUOTE_ERROR; - } - - *path = malloc(strlen(cp) + 1); - if(*path == NULL) - return CURLE_OUT_OF_MEMORY; - - /* Check for quoted filenames */ - if(*cp == '\"' || *cp == '\'') { - quot = *cp++; - - /* Search for terminating quote, unescape some chars */ - for(i = j = 0; i <= strlen(cp); i++) { - if(cp[i] == quot) { /* Found quote */ - i++; - (*path)[j] = '\0'; - break; - } - if(cp[i] == '\0') { /* End of string */ - /*error("Unterminated quote");*/ - goto fail; - } - if(cp[i] == '\\') { /* Escaped characters */ - i++; - if(cp[i] != '\'' && cp[i] != '\"' && - cp[i] != '\\') { - /*error("Bad escaped character '\\%c'", - cp[i]);*/ - goto fail; - } - } - (*path)[j++] = cp[i]; - } - - if(j == 0) { - /*error("Empty quotes");*/ - goto fail; - } - *cpp = cp + i + strspn(cp + i, WHITESPACE); - } - else { - /* Read to end of filename */ - end = strpbrk(cp, WHITESPACE); - if(end == NULL) - end = strchr(cp, '\0'); - *cpp = end + strspn(end, WHITESPACE); - - memcpy(*path, cp, end - cp); - (*path)[end - cp] = '\0'; - } - return CURLE_OK; - - fail: - Curl_safefree(*path); - return CURLE_QUOTE_ERROR; -} - - static const char *sftp_libssh2_strerror(int err) { switch(err) { diff --git a/Utilities/cmcurl/lib/ssh.h b/Utilities/cmcurl/lib/ssh.h index b350dcf..1c13550 100644 --- a/Utilities/cmcurl/lib/ssh.h +++ b/Utilities/cmcurl/lib/ssh.h @@ -24,9 +24,12 @@ #include "curl_setup.h" -#ifdef HAVE_LIBSSH2_H +#if defined(HAVE_LIBSSH2_H) #include <libssh2.h> #include <libssh2_sftp.h> +#elif defined(HAVE_LIBSSH_LIBSSH_H) +#include <libssh/libssh.h> +#include <libssh/sftp.h> #endif /* HAVE_LIBSSH2_H */ /**************************************************************************** @@ -51,6 +54,7 @@ typedef enum { SSH_AUTH_HOST, SSH_AUTH_KEY_INIT, SSH_AUTH_KEY, + SSH_AUTH_GSSAPI, SSH_AUTH_DONE, SSH_SFTP_INIT, SSH_SFTP_REALPATH, /* Last state in SSH-CONNECT */ @@ -86,6 +90,7 @@ typedef enum { SSH_SCP_TRANS_INIT, /* First state in SCP-DO */ SSH_SCP_UPLOAD_INIT, SSH_SCP_DOWNLOAD_INIT, + SSH_SCP_DOWNLOAD, SSH_SCP_DONE, SSH_SCP_SEND_EOF, SSH_SCP_WAIT_EOF, @@ -109,7 +114,8 @@ struct SSHPROTO { struct */ struct ssh_conn { const char *authlist; /* List of auth. methods, managed by libssh2 */ -#ifdef USE_LIBSSH2 + + /* common */ const char *passphrase; /* pass-phrase to use */ char *rsa_pub; /* path name */ char *rsa; /* path name */ @@ -120,16 +126,11 @@ struct ssh_conn { struct curl_slist *quote_item; /* for the quote option */ char *quote_path1; /* two generic pointers for the QUOTE stuff */ char *quote_path2; - LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */ + bool acceptfail; /* used by the SFTP_QUOTE (continue if quote command fails) */ char *homedir; /* when doing SFTP we figure out home dir in the connect phase */ - - /* Here's a set of struct members used by the SFTP_READDIR state */ - LIBSSH2_SFTP_ATTRIBUTES readdir_attrs; - char *readdir_filename; - char *readdir_longentry; int readdir_len, readdir_totalLen, readdir_currLen; char *readdir_line; char *readdir_linkPath; @@ -139,11 +140,42 @@ struct ssh_conn { second attempt has been made to change to/create a directory */ char *slash_pos; /* used by the SFTP_CREATE_DIRS state */ + + int orig_waitfor; /* default READ/WRITE bits wait for */ + +#if defined(USE_LIBSSH) +/* our variables */ + unsigned kbd_state; /* 0 or 1 */ + ssh_key privkey; + ssh_key pubkey; + int auth_methods; + ssh_session ssh_session; + ssh_scp scp_session; + sftp_session sftp_session; + sftp_file sftp_file; + sftp_dir sftp_dir; + + unsigned sftp_recv_state; /* 0 or 1 */ + int sftp_file_index; /* for async read */ + sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */ + sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */ + sftp_attributes quote_attrs; /* used by the SFTP_QUOTE state */ + + const char *readdir_filename; /* points within readdir_attrs */ + const char *readdir_longentry; + char *readdir_tmp; +#elif defined(USE_LIBSSH2) + char *readdir_filename; + char *readdir_longentry; + + LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */ + + /* Here's a set of struct members used by the SFTP_READDIR state */ + LIBSSH2_SFTP_ATTRIBUTES readdir_attrs; LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ LIBSSH2_SFTP *sftp_session; /* SFTP handle */ LIBSSH2_SFTP_HANDLE *sftp_handle; - int orig_waitfor; /* default READ/WRITE bits wait for */ #ifdef HAVE_LIBSSH2_AGENT_API LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */ @@ -156,10 +188,17 @@ struct ssh_conn { #ifdef HAVE_LIBSSH2_KNOWNHOST_API LIBSSH2_KNOWNHOSTS *kh; #endif -#endif /* USE_LIBSSH2 */ +#endif /* USE_LIBSSH */ }; -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH) + +#define CURL_LIBSSH_VERSION ssh_version(0) + +extern const struct Curl_handler Curl_handler_scp; +extern const struct Curl_handler Curl_handler_sftp; + +#elif defined(USE_LIBSSH2) /* Feature detection based on version numbers to better work with non-configure platforms */ @@ -190,6 +229,14 @@ struct ssh_conn { #define HAVE_LIBSSH2_SESSION_HANDSHAKE 1 #endif +#ifdef HAVE_LIBSSH2_VERSION +/* get it run-time if possible */ +#define CURL_LIBSSH2_VERSION libssh2_version(0) +#else +/* use build-time if run-time not possible */ +#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION +#endif + extern const struct Curl_handler Curl_handler_scp; extern const struct Curl_handler Curl_handler_sftp; diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c index 807fc54..3636477 100644 --- a/Utilities/cmcurl/lib/strtoofft.c +++ b/Utilities/cmcurl/lib/strtoofft.c @@ -219,7 +219,10 @@ CURLofft curlx_strtoofft(const char *str, char **endp, int base, curl_off_t number; errno = 0; *num = 0; /* clear by default */ - while(str && *str && ISSPACE(*str)) + + DEBUGASSERT(str); + + while(*str && ISSPACE(*str)) str++; if('-' == *str) { if(endp) diff --git a/Utilities/cmcurl/lib/strtoofft.h b/Utilities/cmcurl/lib/strtoofft.h index 244411a..be19cd7 100644 --- a/Utilities/cmcurl/lib/strtoofft.h +++ b/Utilities/cmcurl/lib/strtoofft.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -40,14 +40,6 @@ * of 'long' the conversion function to use is strtol(). */ -#if (SIZEOF_CURL_OFF_T == 4) -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) -#else - /* assume CURL_SIZEOF_CURL_OFF_T == 8 */ -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) -#endif -#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) - typedef enum { CURL_OFFT_OK, /* parsed fine */ CURL_OFFT_FLOW, /* over or underflow */ diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index a7bed3d..48b134e 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -1560,8 +1560,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } if(data->set.timeout) { - now = Curl_tvnow(); - if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; @@ -1678,8 +1678,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } /* poll switch statement */ if(data->set.timeout) { - now = Curl_tvnow(); - if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index 4e599fd..20dc600 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -200,7 +200,7 @@ const struct Curl_handler Curl_handler_tftp = { static CURLcode tftp_set_timeouts(tftp_state_data_t *state) { time_t maxtime, timeout; - time_t timeout_ms; + timediff_t timeout_ms; bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; time(&state->start_time); @@ -1293,7 +1293,7 @@ static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done) if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(conn->data, Curl_tvnow()); + result = Curl_speedcheck(conn->data, Curl_now()); } return result; } diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index d7207b3..66f923a 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -24,7 +24,7 @@ #if defined(WIN32) && !defined(MSDOS) -struct curltime curlx_tvnow(void) +struct curltime Curl_now(void) { /* ** GetTickCount() is available on _all_ Windows versions from W95 up @@ -48,7 +48,7 @@ struct curltime curlx_tvnow(void) #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) -struct curltime curlx_tvnow(void) +struct curltime Curl_now(void) { /* ** clock_gettime() is granted to be increased monotonically when the @@ -84,9 +84,40 @@ struct curltime curlx_tvnow(void) return cnow; } +#elif defined(HAVE_MACH_ABSOLUTE_TIME) + +#include <stdint.h> +#include <mach/mach_time.h> + +struct curltime Curl_now(void) +{ + /* + ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which + ** returns time in Mach "absolute time units," which are platform-dependent. + ** To convert to nanoseconds, one must use conversion factors specified by + ** mach_timebase_info(). + */ + static mach_timebase_info_data_t timebase; + struct curltime cnow; + uint64_t usecs; + + if(0 == timebase.denom) + (void) mach_timebase_info(&timebase); + + usecs = mach_absolute_time(); + usecs *= timebase.numer; + usecs /= timebase.denom; + usecs /= 1000; + + cnow.tv_sec = usecs / 1000000; + cnow.tv_usec = usecs % 1000000; + + return cnow; +} + #elif defined(HAVE_GETTIMEOFDAY) -struct curltime curlx_tvnow(void) +struct curltime Curl_now(void) { /* ** gettimeofday() is not granted to be increased monotonically, due to @@ -103,7 +134,7 @@ struct curltime curlx_tvnow(void) #else -struct curltime curlx_tvnow(void) +struct curltime Curl_now(void) { /* ** time() returns the value of time in seconds since the Epoch. @@ -116,47 +147,40 @@ struct curltime curlx_tvnow(void) #endif +#if SIZEOF_TIME_T < 8 +#define TIME_MAX INT_MAX +#define TIME_MIN INT_MIN +#else +#define TIME_MAX 9223372036854775807LL +#define TIME_MIN -9223372036854775807LL +#endif + /* - * Make sure that the first argument is the more recent time, as otherwise - * we'll get a weird negative time-diff back... - * - * Returns: the time difference in number of milliseconds. For large diffs it - * returns 0x7fffffff on 32bit time_t systems. + * Returns: time difference in number of milliseconds. For too large diffs it + * returns max value. * * @unittest: 1323 */ -time_t curlx_tvdiff(struct curltime newer, struct curltime older) +timediff_t Curl_timediff(struct curltime newer, struct curltime older) { -#if SIZEOF_TIME_T < 8 - /* for 32bit time_t systems, add a precaution to avoid overflow for really - big time differences */ - time_t diff = newer.tv_sec-older.tv_sec; - if(diff >= (0x7fffffff/1000)) - return 0x7fffffff; -#endif - return (newer.tv_sec-older.tv_sec)*1000+ - (int)(newer.tv_usec-older.tv_usec)/1000; + timediff_t diff = newer.tv_sec-older.tv_sec; + if(diff >= (TIME_MAX/1000)) + return TIME_MAX; + else if(diff <= (TIME_MIN/1000)) + return TIME_MIN; + return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000; } /* - * Make sure that the first argument is the more recent time, as otherwise - * we'll get a weird negative time-diff back... - * - * Returns: the time difference in number of microseconds. For too large diffs - * it returns max value. + * Returns: time difference in number of microseconds. For too large diffs it + * returns max value. */ -time_t Curl_tvdiff_us(struct curltime newer, struct curltime older) +timediff_t Curl_timediff_us(struct curltime newer, struct curltime older) { - time_t diff = newer.tv_sec-older.tv_sec; -#if SIZEOF_TIME_T < 8 - /* for 32bit time_t systems */ - if(diff >= (0x7fffffff/1000000)) - return 0x7fffffff; -#else - /* for 64bit time_t systems */ - if(diff >= (0x7fffffffffffffffLL/1000000)) - return 0x7fffffffffffffffLL; -#endif - return (newer.tv_sec-older.tv_sec)*1000000+ - (int)(newer.tv_usec-older.tv_usec); + timediff_t diff = newer.tv_sec-older.tv_sec; + if(diff >= (TIME_MAX/1000000)) + return TIME_MAX; + else if(diff <= (TIME_MIN/1000000)) + return TIME_MIN; + return diff * 1000000 + newer.tv_usec-older.tv_usec; } diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h index 1ee4b30..fb3f680 100644 --- a/Utilities/cmcurl/lib/timeval.h +++ b/Utilities/cmcurl/lib/timeval.h @@ -22,19 +22,20 @@ * ***************************************************************************/ -/* - * CAUTION: this header is designed to work when included by the app-side - * as well as the library. Do not mix with library internals! - */ - #include "curl_setup.h" +#if SIZEOF_TIME_T < 8 +typedef int timediff_t; +#else +typedef curl_off_t timediff_t; +#endif + struct curltime { - time_t tv_sec; /* seconds */ - unsigned int tv_usec; /* microseconds */ + time_t tv_sec; /* seconds */ + int tv_usec; /* microseconds */ }; -struct curltime curlx_tvnow(void); +struct curltime Curl_now(void); /* * Make sure that the first argument (t1) is the more recent time and t2 is @@ -42,7 +43,7 @@ struct curltime curlx_tvnow(void); * * Returns: the time difference in number of milliseconds. */ -time_t curlx_tvdiff(struct curltime t1, struct curltime t2); +timediff_t Curl_timediff(struct curltime t1, struct curltime t2); /* * Make sure that the first argument (t1) is the more recent time and t2 is @@ -50,12 +51,6 @@ time_t curlx_tvdiff(struct curltime t1, struct curltime t2); * * Returns: the time difference in number of microseconds. */ -time_t Curl_tvdiff_us(struct curltime newer, struct curltime older); - -/* These two defines below exist to provide the older API for library - internals only. */ -#define Curl_tvnow() curlx_tvnow() -#define Curl_tvdiff(x,y) curlx_tvdiff(x,y) +timediff_t Curl_timediff_us(struct curltime newer, struct curltime older); #endif /* HEADER_CURL_TIMEVAL_H */ - diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 8e66d0d..8f15b1a 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -238,9 +238,11 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp) } #endif /* CURL_DOES_CONVERSIONS */ - if((nread - hexlen) == 0) + if((nread - hexlen) == 0) { /* mark this as done once this chunk is transferred */ data->req.upload_done = TRUE; + infof(data, "Signaling end of chunked upload via terminating chunk.\n"); + } nread += (int)strlen(endofline_native); /* for the added end of line */ } @@ -490,7 +492,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, Curl_pgrsTime(data, TIMER_STARTTRANSFER); if(k->exp100 > EXP100_SEND_DATA) /* set time stamp to compare with when waiting for the 100 */ - k->start100 = Curl_tvnow(); + k->start100 = Curl_now(); } *didwhat |= KEEP_RECV; @@ -777,48 +779,19 @@ static CURLcode readwrite_data(struct Curl_easy *data, in http_chunks.c. Make sure that ALL_CONTENT_ENCODINGS contains all the encodings handled here. */ -#ifdef HAVE_LIBZ - switch(conn->data->set.http_ce_skip ? - IDENTITY : k->auto_decoding) { - case IDENTITY: -#endif - /* This is the default when the server sends no - Content-Encoding header. See Curl_readwrite_init; the - memset() call initializes k->auto_decoding to zero. */ + if(conn->data->set.http_ce_skip || !k->writer_stack) { if(!k->ignorebody) { - #ifndef CURL_DISABLE_POP3 - if(conn->handler->protocol&PROTO_FAMILY_POP3) + if(conn->handler->protocol & PROTO_FAMILY_POP3) result = Curl_pop3_write(conn, k->str, nread); else #endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str, nread); } -#ifdef HAVE_LIBZ - break; - - case DEFLATE: - /* Assume CLIENTWRITE_BODY; headers are not encoded. */ - if(!k->ignorebody) - result = Curl_unencode_deflate_write(conn, k, nread); - break; - - case GZIP: - /* Assume CLIENTWRITE_BODY; headers are not encoded. */ - if(!k->ignorebody) - result = Curl_unencode_gzip_write(conn, k, nread); - break; - - default: - failf(data, "Unrecognized content encoding type. " - "libcurl understands `identity', `deflate' and `gzip' " - "content encodings."); - result = CURLE_BAD_CONTENT_ENCODING; - break; } -#endif + else + result = Curl_unencode_write(conn, k->writer_stack, k->str, nread); } k->badheader = HEADER_NORMAL; /* taken care of now */ @@ -925,7 +898,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data, go into the Expect: 100 state and await such a header */ k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */ k->keepon &= ~KEEP_SEND; /* disable writing */ - k->start100 = Curl_tvnow(); /* timeout count starts now */ + k->start100 = Curl_now(); /* timeout count starts now */ *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ /* set a timeout for the multi interface */ @@ -1046,7 +1019,8 @@ static CURLcode readwrite_upload(struct Curl_easy *data, k->writebytecount += bytes_written; - if(k->writebytecount == data->state.infilesize) { + if((!k->upload_chunky || k->forbidchunk) && + (k->writebytecount == data->state.infilesize)) { /* we have sent all data we were supposed to */ k->upload_done = TRUE; infof(data, "We are completely uploaded and fine\n"); @@ -1150,7 +1124,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, return result; } - k->now = Curl_tvnow(); + k->now = Curl_now(); if(didwhat) { /* Update read/write counters */ if(k->bytecountp) @@ -1174,7 +1148,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, */ - time_t ms = Curl_tvdiff(k->now, k->start100); + timediff_t ms = Curl_timediff(k->now, k->start100); if(ms >= data->set.expect_100_timeout) { /* we've waited long enough, continue anyway */ k->exp100 = EXP100_SEND_DATA; @@ -1198,13 +1172,14 @@ CURLcode Curl_readwrite(struct connectdata *conn, failf(data, "Operation timed out after %ld milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount, - k->size); + Curl_timediff(k->now, data->progress.t_startsingle), + k->bytecount, k->size); } else { failf(data, "Operation timed out after %ld milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_tvdiff(k->now, data->progress.t_startsingle), k->bytecount); + Curl_timediff(k->now, data->progress.t_startsingle), + k->bytecount); } return CURLE_OPERATION_TIMEDOUT; } @@ -1343,6 +1318,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(result) return result; + data->state.wildcardmatch = data->set.wildcard_enabled; data->set.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ data->state.errorbuf = FALSE; /* no error has occurred */ @@ -1400,7 +1376,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authhost.picked &= data->state.authhost.want; data->state.authproxy.picked &= data->state.authproxy.want; - if(data->set.wildcardmatch) { + if(data->state.wildcardmatch) { struct WildcardData *wc = &data->wildcard; if(wc->state < CURLWC_INIT) { result = Curl_wildcard_init(wc); /* init wildcard structures */ @@ -2049,7 +2025,7 @@ Curl_setup_transfer( (http->sending == HTTPSEND_BODY)) { /* wait with write until we either got 100-continue or a timeout */ k->exp100 = EXP100_AWAITING_CONTINUE; - k->start100 = Curl_tvnow(); + k->start100 = Curl_now(); /* Set a timeout for the multi interface. Add the inaccuracy margin so that we don't fire slightly too early and get denied to run. */ diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 584635b..74813e8 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,9 +55,7 @@ #error "We can't compile without socket() support!" #endif -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #ifdef USE_LIBIDN2 #include <idn2.h> @@ -120,15 +118,13 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "pipeline.h" #include "dotdot.h" #include "strdup.h" +#include "setopt.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -/* Local static prototypes */ -static struct connectdata * -find_oldest_idle_connection_in_bundle(struct Curl_easy *data, - struct connectbundle *bundle); static void conn_free(struct connectdata *conn); static void free_fixed_hostname(struct hostname *host); static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke); @@ -136,15 +132,8 @@ static CURLcode parse_url_login(struct Curl_easy *data, struct connectdata *conn, char **userptr, char **passwdptr, char **optionsptr); -static CURLcode parse_login_details(const char *login, const size_t len, - char **userptr, char **passwdptr, - char **optionsptr); static unsigned int get_protocol_family(unsigned int protocol); -#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE -#define READBUFFER_MAX CURL_MAX_READ_SIZE -#define READBUFFER_MIN 1024 - /* Some parts of the code (e.g. chunked encoding) assume this buffer has at * more than just a few bytes to play with. Don't let it become too small or * bad things will happen. @@ -201,8 +190,11 @@ static const struct Curl_handler * const protocols[] = { &Curl_handler_tftp, #endif -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) &Curl_handler_scp, +#endif + +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) &Curl_handler_sftp, #endif @@ -299,98 +291,8 @@ void Curl_freeset(struct Curl_easy *data) data->change.url_alloc = FALSE; } data->change.url = NULL; -} - -static CURLcode setstropt(char **charp, const char *s) -{ - /* Release the previous storage at `charp' and replace by a dynamic storage - copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - - Curl_safefree(*charp); - - if(s) { - char *str = strdup(s); - - if(!str) - return CURLE_OUT_OF_MEMORY; - - *charp = str; - } - - return CURLE_OK; -} - -static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) -{ - CURLcode result = CURLE_OK; - char *user = NULL; - char *passwd = NULL; - - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ - if(option) { - result = parse_login_details(option, strlen(option), - (userp ? &user : NULL), - (passwdp ? &passwd : NULL), - NULL); - } - - if(!result) { - /* Store the username part of option if required */ - if(userp) { - if(!user && option && option[0] == ':') { - /* Allocate an empty string instead of returning NULL as user name */ - user = strdup(""); - if(!user) - result = CURLE_OUT_OF_MEMORY; - } - - Curl_safefree(*userp); - *userp = user; - } - - /* Store the password part of option if required */ - if(passwdp) { - Curl_safefree(*passwdp); - *passwdp = passwd; - } - } - - return result; -} - -CURLcode Curl_dupset(struct Curl_easy *dst, struct Curl_easy *src) -{ - CURLcode result = CURLE_OK; - enum dupstring i; - - /* Copy src->set into dst->set first, then deal with the strings - afterwards */ - dst->set = src->set; - - /* clear all string pointers first */ - memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); - /* duplicate all strings */ - for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { - result = setstropt(&dst->set.str[i], src->set.str[i]); - if(result) - return result; - } - - /* duplicate memory areas pointed to */ - i = STRING_COPYPOSTFIELDS; - if(src->set.postfieldsize && src->set.str[i]) { - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); - if(!dst->set.str[i]) - return CURLE_OUT_OF_MEMORY; - /* point to the new copy */ - dst->set.postfields = dst->set.str[i]; - } - - return CURLE_OK; + Curl_mime_cleanpart(&data->set.mimepost); } /* @@ -480,8 +382,6 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_http2_cleanup_dependencies(data); Curl_convert_close(data); - Curl_mime_cleanpart(&data->set.mimepost); - /* No longer a dirty share, if it exists */ if(data->share) { Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); @@ -489,12 +389,8 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } - if(data->set.wildcardmatch) { - /* destruct wildcard structures if it is needed */ - struct WildcardData *wc = &data->wildcard; - Curl_wildcard_dtor(wc); - } - + /* destruct wildcard structures if it is needed */ + Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); free(data); return CURLE_OK; @@ -504,8 +400,9 @@ CURLcode Curl_close(struct Curl_easy *data) * Initialize the UserDefined fields within a Curl_easy. * This may be safely called on a new or existing Curl_easy. */ -CURLcode Curl_init_userdefined(struct UserDefined *set) +CURLcode Curl_init_userdefined(struct Curl_easy *data) { + struct UserDefined *set = &data->set; CURLcode result = CURLE_OK; set->out = stdout; /* default output to stdout */ @@ -555,6 +452,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) /* make libcurl quiet by default: */ set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ + Curl_mime_initpart(&set->mimepost, data); + /* * libcurl 7.10 introduced SSL verification *by default*! This needs to be * switched off unless wanted. @@ -591,25 +490,25 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) /* This is our preferred CA cert bundle/path since install time */ #if defined(CURL_CA_BUNDLE) - result = setstropt(&set->str[STRING_SSL_CAFILE_ORIG], CURL_CA_BUNDLE); + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_ORIG], CURL_CA_BUNDLE); if(result) return result; - result = setstropt(&set->str[STRING_SSL_CAFILE_PROXY], CURL_CA_BUNDLE); + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], CURL_CA_BUNDLE); if(result) return result; #endif #if defined(CURL_CA_PATH) - result = setstropt(&set->str[STRING_SSL_CAPATH_ORIG], CURL_CA_PATH); + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_ORIG], CURL_CA_PATH); if(result) return result; - result = setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); if(result) return result; #endif - set->wildcardmatch = FALSE; + set->wildcard_enabled = FALSE; set->chunk_bgn = ZERO_NULL; set->chunk_end = ZERO_NULL; @@ -670,32 +569,29 @@ CURLcode Curl_open(struct Curl_easy **curl) DEBUGF(fprintf(stderr, "Error: malloc of buffer failed\n")); result = CURLE_OUT_OF_MEMORY; } - - Curl_mime_initpart(&data->set.mimepost, data); - - data->state.headerbuff = malloc(HEADERSIZE); - if(!data->state.headerbuff) { - DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); - result = CURLE_OUT_OF_MEMORY; - } else { - result = Curl_init_userdefined(&data->set); - - data->state.headersize = HEADERSIZE; - - Curl_convert_init(data); + data->state.headerbuff = malloc(HEADERSIZE); + if(!data->state.headerbuff) { + DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); + result = CURLE_OUT_OF_MEMORY; + } + else { + result = Curl_init_userdefined(data); - Curl_initinfo(data); + data->state.headersize = HEADERSIZE; + Curl_convert_init(data); + Curl_initinfo(data); - /* most recent connection is not yet defined */ - data->state.lastconnect = NULL; + /* most recent connection is not yet defined */ + data->state.lastconnect = NULL; - data->progress.flags |= PGRS_HIDE; - data->state.current_speed = -1; /* init to negative == impossible */ - data->set.fnmatch = ZERO_NULL; - data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ + data->set.fnmatch = ZERO_NULL; + data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ - Curl_http2_init_state(&data->state); + Curl_http2_init_state(&data->state); + } } if(result) { @@ -712,2269 +608,6 @@ CURLcode Curl_open(struct Curl_easy **curl) return result; } -#define C_SSLVERSION_VALUE(x) (x & 0xffff) -#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000) - -CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option, - va_list param) -{ - char *argptr; - CURLcode result = CURLE_OK; - long arg; -#ifndef CURL_DISABLE_HTTP - curl_off_t bigsize; -#endif - - switch(option) { - case CURLOPT_DNS_CACHE_TIMEOUT: - data->set.dns_cache_timeout = va_arg(param, long); - break; - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* remember we want this enabled */ - arg = va_arg(param, long); - data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE; - break; - case CURLOPT_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection */ - result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection for proxy */ - result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_RANDOM_FILE: - /* - * This is the path name to a file that contains random data to seed - * the random SSL stuff with. The file is only used for reading. - */ - result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE], - va_arg(param, char *)); - break; - case CURLOPT_EGDSOCKET: - /* - * The Entropy Gathering Daemon socket pathname - */ - result = setstropt(&data->set.str[STRING_SSL_EGDSOCKET], - va_arg(param, char *)); - break; - case CURLOPT_MAXCONNECTS: - /* - * Set the absolute number of maximum simultaneous alive connection that - * libcurl is allowed to have. - */ - data->set.maxconnects = va_arg(param, long); - break; - case CURLOPT_FORBID_REUSE: - /* - * When this transfer is done, it must not be left to be reused by a - * subsequent transfer but shall be closed immediately. - */ - data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_FRESH_CONNECT: - /* - * This transfer shall not use a previously cached connection but - * should be made with a fresh new connect! - */ - data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_VERBOSE: - /* - * Verbose means infof() calls that give a lot of information about - * the connection and transfer procedures as well as internal choices. - */ - data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_HEADER: - /* - * Set to include the header in the general data output stream. - */ - data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_NOPROGRESS: - /* - * Shut off the internal supported progress meter - */ - data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE; - if(data->set.hide_progress) - data->progress.flags |= PGRS_HIDE; - else - data->progress.flags &= ~PGRS_HIDE; - break; - case CURLOPT_NOBODY: - /* - * Do not include the body part in the output data stream. - */ - data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_FAILONERROR: - /* - * Don't output the >=400 error code HTML-page, but instead only - * return error. - */ - data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_KEEP_SENDING_ON_ERROR: - data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - case CURLOPT_UPLOAD: - case CURLOPT_PUT: - /* - * We want to sent data to the remote host. If this is HTTP, that equals - * using the PUT request. - */ - data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; - if(data->set.upload) { - /* If this is HTTP, PUT is what's needed to "upload" */ - data->set.httpreq = HTTPREQ_PUT; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - /* In HTTP, the opposite of upload is GET (unless NOBODY is true as - then this can be changed to HEAD later on) */ - data->set.httpreq = HTTPREQ_GET; - break; - case CURLOPT_REQUEST_TARGET: - result = setstropt(&data->set.str[STRING_TARGET], - va_arg(param, char *)); - break; - case CURLOPT_FILETIME: - /* - * Try to get the file time of the remote document. The time will - * later (possibly) become available using curl_easy_getinfo(). - */ - data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_FTP_CREATE_MISSING_DIRS: - /* - * An FTP option that modifies an upload to create missing directories on - * the server. - */ - switch(va_arg(param, long)) { - case 0: - data->set.ftp_create_missing_dirs = 0; - break; - case 1: - data->set.ftp_create_missing_dirs = 1; - break; - case 2: - data->set.ftp_create_missing_dirs = 2; - break; - default: - /* reserve other values for future use */ - result = CURLE_UNKNOWN_OPTION; - break; - } - break; - case CURLOPT_SERVER_RESPONSE_TIMEOUT: - /* - * Option that specifies how quickly an server response must be obtained - * before it is considered failure. For pingpong protocols. - */ - data->set.server_response_timeout = va_arg(param, long) * 1000; - break; - case CURLOPT_TFTP_NO_OPTIONS: - /* - * Option that prevents libcurl from sending TFTP option requests to the - * server. - */ - data->set.tftp_no_options = va_arg(param, long) != 0; - break; - case CURLOPT_TFTP_BLKSIZE: - /* - * TFTP option that specifies the block size to use for data transmission. - */ - data->set.tftp_blksize = va_arg(param, long); - break; - case CURLOPT_DIRLISTONLY: - /* - * An option that changes the command to one that asks for a list - * only, no file info details. - */ - data->set.ftp_list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_APPEND: - /* - * We want to upload and append to an existing file. - */ - data->set.ftp_append = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_FTP_FILEMETHOD: - /* - * How do access files over FTP. - */ - data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long); - break; - case CURLOPT_NETRC: - /* - * Parse the $HOME/.netrc file - */ - data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long); - break; - case CURLOPT_NETRC_FILE: - /* - * Use this file instead of the $HOME/.netrc file - */ - result = setstropt(&data->set.str[STRING_NETRC_FILE], - va_arg(param, char *)); - break; - case CURLOPT_TRANSFERTEXT: - /* - * This option was previously named 'FTPASCII'. Renamed to work with - * more protocols than merely FTP. - * - * Transfer using ASCII (instead of BINARY). - */ - data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_TIMECONDITION: - /* - * Set HTTP time condition. This must be one of the defines in the - * curl/curl.h header file. - */ - data->set.timecondition = (curl_TimeCond)va_arg(param, long); - break; - case CURLOPT_TIMEVALUE: - /* - * This is the value to compare with the remote document with the - * method set with CURLOPT_TIMECONDITION - */ - data->set.timevalue = (time_t)va_arg(param, long); - break; - case CURLOPT_SSLVERSION: - /* - * Set explicit SSL version to try to connect with, as some SSL - * implementations are lame. - */ -#ifdef USE_SSL - arg = va_arg(param, long); - data->set.ssl.primary.version = C_SSLVERSION_VALUE(arg); - data->set.ssl.primary.version_max = C_SSLVERSION_MAX_VALUE(arg); -#else - result = CURLE_UNKNOWN_OPTION; -#endif - break; - case CURLOPT_PROXY_SSLVERSION: - /* - * Set explicit SSL version to try to connect with for proxy, as some SSL - * implementations are lame. - */ -#ifdef USE_SSL - arg = va_arg(param, long); - data->set.proxy_ssl.primary.version = C_SSLVERSION_VALUE(arg); - data->set.proxy_ssl.primary.version_max = C_SSLVERSION_MAX_VALUE(arg); -#else - result = CURLE_UNKNOWN_OPTION; -#endif - break; - -#ifndef CURL_DISABLE_HTTP - case CURLOPT_AUTOREFERER: - /* - * Switch on automatic referer that gets set if curl follows locations. - */ - data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_ACCEPT_ENCODING: - /* - * String to use at the value of Accept-Encoding header. - * - * If the encoding is set to "" we use an Accept-Encoding header that - * encompasses all the encodings we support. - * If the encoding is set to NULL we don't send an Accept-Encoding header - * and ignore an received Content-Encoding header. - * - */ - argptr = va_arg(param, char *); - result = setstropt(&data->set.str[STRING_ENCODING], - (argptr && !*argptr)? - ALL_CONTENT_ENCODINGS: argptr); - break; - - case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - - case CURLOPT_FOLLOWLOCATION: - /* - * Follow Location: header hints on a HTTP-server. - */ - data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_UNRESTRICTED_AUTH: - /* - * Send authentication (user+password) when following locations, even when - * hostname changed. - */ - data->set.http_disable_hostname_check_before_authentication = - (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_MAXREDIRS: - /* - * The maximum amount of hops you allow curl to follow Location: - * headers. This should mostly be used to detect never-ending loops. - */ - data->set.maxredirs = va_arg(param, long); - break; - - case CURLOPT_POSTREDIR: - { - /* - * Set the behaviour of POST when redirecting - * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302 - * CURL_REDIR_POST_301 - POST is kept as POST after 301 - * CURL_REDIR_POST_302 - POST is kept as POST after 302 - * CURL_REDIR_POST_303 - POST is kept as POST after 303 - * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 - * other - POST is kept as POST after 301 and 302 - */ - arg = va_arg(param, long); - data->set.keep_post = arg & CURL_REDIR_POST_ALL; - } - break; - - case CURLOPT_POST: - /* Does this option serve a purpose anymore? Yes it does, when - CURLOPT_POSTFIELDS isn't used and the POST data is read off the - callback! */ - if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_POST; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - data->set.httpreq = HTTPREQ_GET; - break; - - case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - argptr = va_arg(param, char *); - - if(!argptr || data->set.postfieldsize == -1) - result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); - else { - /* - * Check that requested length does not overflow the size_t type. - */ - - if((data->set.postfieldsize < 0) || - ((sizeof(curl_off_t) != sizeof(size_t)) && - (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) - result = CURLE_OUT_OF_MEMORY; - else { - char *p; - - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and - to mark that postfields is used rather than read function or - form data. - */ - p = malloc((size_t)(data->set.postfieldsize? - data->set.postfieldsize:1)); - - if(!p) - result = CURLE_OUT_OF_MEMORY; - else { - if(data->set.postfieldsize) - memcpy(p, argptr, (size_t)data->set.postfieldsize); - - data->set.str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.httpreq = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDS: - /* - * Like above, but use static data instead of copying it. - */ - data->set.postfields = va_arg(param, void *); - /* Release old copied data. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.httpreq = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDSIZE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, long); - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; - - case CURLOPT_POSTFIELDSIZE_LARGE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, curl_off_t); - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; - - case CURLOPT_HTTPPOST: - /* - * Set to make us do HTTP POST - */ - data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.httpreq = HTTPREQ_POST_FORM; - data->set.opt_no_body = FALSE; /* this is implied */ - break; -#endif /* CURL_DISABLE_HTTP */ - - case CURLOPT_MIMEPOST: - /* - * Set to make us do MIME/form POST - */ - result = curl_mime_subparts(&data->set.mimepost, - va_arg(param, curl_mime *)); - if(!result) { - data->set.mimepost.freefunc = NULL; /* Avoid free upon easy cleanup. */ - data->set.httpreq = HTTPREQ_POST_MIME; - data->set.opt_no_body = FALSE; /* this is implied */ - } - break; - - case CURLOPT_REFERER: - /* - * String to set in the HTTP Referer: field. - */ - if(data->change.referer_alloc) { - Curl_safefree(data->change.referer); - data->change.referer_alloc = FALSE; - } - result = setstropt(&data->set.str[STRING_SET_REFERER], - va_arg(param, char *)); - data->change.referer = data->set.str[STRING_SET_REFERER]; - break; - - case CURLOPT_USERAGENT: - /* - * String to use in the HTTP User-Agent field - */ - result = setstropt(&data->set.str[STRING_USERAGENT], - va_arg(param, char *)); - break; - - case CURLOPT_HTTPHEADER: - /* - * Set a list with HTTP headers to use (or replace internals with) - */ - data->set.headers = va_arg(param, struct curl_slist *); - break; - -#ifndef CURL_DISABLE_HTTP - case CURLOPT_PROXYHEADER: - /* - * Set a list with proxy headers to use (or replace internals with) - * - * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a - * long time we remain doing it this way until CURLOPT_PROXYHEADER is - * used. As soon as this option has been used, if set to anything but - * NULL, custom headers for proxies are only picked from this list. - * - * Set this option to NULL to restore the previous behavior. - */ - data->set.proxyheaders = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_HEADEROPT: - /* - * Set header option. - */ - arg = va_arg(param, long); - data->set.sep_headers = (arg & CURLHEADER_SEPARATE)? TRUE: FALSE; - break; - - case CURLOPT_HTTP200ALIASES: - /* - * Set a list of aliases for HTTP 200 in response header - */ - data->set.http200aliases = va_arg(param, struct curl_slist *); - break; - -#if !defined(CURL_DISABLE_COOKIES) - case CURLOPT_COOKIE: - /* - * Cookie string to send to the remote server in the request. - */ - result = setstropt(&data->set.str[STRING_COOKIE], - va_arg(param, char *)); - break; - - case CURLOPT_COOKIEFILE: - /* - * Set cookie file to read and parse. Can be used multiple times. - */ - argptr = (char *)va_arg(param, void *); - if(argptr) { - struct curl_slist *cl; - /* append the cookie file name to the list of file names, and deal with - them later */ - cl = curl_slist_append(data->change.cookielist, argptr); - if(!cl) { - curl_slist_free_all(data->change.cookielist); - data->change.cookielist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->change.cookielist = cl; /* store the list for later use */ - } - break; - - case CURLOPT_COOKIEJAR: - /* - * Set cookie file name to dump all cookies to when we're done. - */ - { - struct CookieInfo *newcookies; - result = setstropt(&data->set.str[STRING_COOKIEJAR], - va_arg(param, char *)); - - /* - * Activate the cookie parser. This may or may not already - * have been made. - */ - newcookies = Curl_cookie_init(data, NULL, data->cookies, - data->set.cookiesession); - if(!newcookies) - result = CURLE_OUT_OF_MEMORY; - data->cookies = newcookies; - } - break; - - case CURLOPT_COOKIESESSION: - /* - * Set this option to TRUE to start a new "cookie session". It will - * prevent the forthcoming read-cookies-from-file actions to accept - * cookies that are marked as being session cookies, as they belong to a - * previous session. - * - * In the original Netscape cookie spec, "session cookies" are cookies - * with no expire date set. RFC2109 describes the same action if no - * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds - * a 'Discard' action that can enforce the discard even for cookies that - * have a Max-Age. - * - * We run mostly with the original cookie spec, as hardly anyone implements - * anything else. - */ - data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_COOKIELIST: - argptr = va_arg(param, char *); - - if(argptr == NULL) - break; - - if(strcasecompare(argptr, "ALL")) { - /* clear all cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearall(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "SESS")) { - /* clear session cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearsess(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "FLUSH")) { - /* flush cookies to file, takes care of the locking */ - Curl_flush_cookies(data, 0); - } - else if(strcasecompare(argptr, "RELOAD")) { - /* reload cookies from file */ - Curl_cookie_loadfiles(data); - break; - } - else { - if(!data->cookies) - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); - - argptr = strdup(argptr); - if(!argptr || !data->cookies) { - result = CURLE_OUT_OF_MEMORY; - free(argptr); - } - else { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - if(checkprefix("Set-Cookie:", argptr)) - /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); - - else - /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); - - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - free(argptr); - } - } - - break; -#endif /* !CURL_DISABLE_COOKIES */ - - case CURLOPT_HTTPGET: - /* - * Set to force us do HTTP GET - */ - if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_GET; - data->set.upload = FALSE; /* switch off upload */ - data->set.opt_no_body = FALSE; /* this is implied */ - } - break; - - case CURLOPT_HTTP_VERSION: - /* - * This sets a requested HTTP version to be used. The value is one of - * the listed enums in curl/curl.h. - */ - arg = va_arg(param, long); -#ifndef USE_NGHTTP2 - if(arg >= CURL_HTTP_VERSION_2) - return CURLE_UNSUPPORTED_PROTOCOL; -#endif - data->set.httpversion = arg; - break; - - case CURLOPT_EXPECT_100_TIMEOUT_MS: - /* - * Time to wait for a response to a HTTP request containing an - * Expect: 100-continue header before sending the data anyway. - */ - data->set.expect_100_timeout = va_arg(param, long); - break; - -#endif /* CURL_DISABLE_HTTP */ - - case CURLOPT_HTTPAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.httpauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE; - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.httpauth = auth; - } - break; - - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - result = setstropt(&data->set.str[STRING_CUSTOMREQUEST], - va_arg(param, char *)); - - /* we don't set - data->set.httpreq = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HTTPPROXYTUNNEL: - /* - * Tunnel operations through the proxy instead of normal proxy use - */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - - case CURLOPT_PROXYPORT: - /* - * Explicitly set HTTP proxy port number. - */ - data->set.proxyport = va_arg(param, long); - break; - - case CURLOPT_PROXYAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.proxyauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE; - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.proxyauth = auth; - } - break; - - case CURLOPT_PROXY: - /* - * Set proxy server:port to use as proxy. - * - * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we don't want to use a proxy - * (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). - */ - result = setstropt(&data->set.str[STRING_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_PRE_PROXY: - /* - * Set proxy server:port to use as SOCKS proxy. - * - * If the proxy is set to "" or NULL we explicitly say that we don't want - * to use the socks proxy. - */ - result = setstropt(&data->set.str[STRING_PRE_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_PROXYTYPE: - /* - * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME - */ - data->set.proxytype = (curl_proxytype)va_arg(param, long); - break; - - case CURLOPT_PROXY_TRANSFER_MODE: - /* - * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy - */ - switch(va_arg(param, long)) { - case 0: - data->set.proxy_transfer_mode = FALSE; - break; - case 1: - data->set.proxy_transfer_mode = TRUE; - break; - default: - /* reserve other values for future use */ - result = CURLE_UNKNOWN_OPTION; - break; - } - break; -#endif /* CURL_DISABLE_PROXY */ - - case CURLOPT_SOCKS5_AUTH: - data->set.socks5auth = va_arg(param, unsigned long); - if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - result = CURLE_NOT_BUILT_IN; - break; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CURLOPT_SOCKS5_GSSAPI_NEC: - /* - * Set flag for NEC SOCK5 support - */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_PROXY_SERVICE_NAME: - /* - * Set proxy authentication service name for Kerberos 5 and SPNEGO - */ - result = setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], - va_arg(param, char *)); - break; -#endif - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_SPNEGO) - case CURLOPT_SERVICE_NAME: - /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO - */ - result = setstropt(&data->set.str[STRING_SERVICE_NAME], - va_arg(param, char *)); - break; - -#endif - - case CURLOPT_HEADERDATA: - /* - * Custom pointer to pass the header write callback function - */ - data->set.writeheader = (void *)va_arg(param, void *); - break; - case CURLOPT_ERRORBUFFER: - /* - * Error buffer provided by the caller to get the human readable - * error string in. - */ - data->set.errorbuffer = va_arg(param, char *); - break; - case CURLOPT_WRITEDATA: - /* - * FILE pointer to write to. Or possibly - * used as argument to the write callback. - */ - data->set.out = va_arg(param, void *); - break; - case CURLOPT_FTPPORT: - /* - * Use FTP PORT, this also specifies which IP address to use - */ - result = setstropt(&data->set.str[STRING_FTPPORT], - va_arg(param, char *)); - data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_SSL_CCC: - data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long); - break; - - case CURLOPT_FTP_SKIP_PASV_IP: - /* - * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the - * bypass of the IP address in PASV responses. - */ - data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_READDATA: - /* - * FILE pointer to read the file to be uploaded from. Or possibly - * used as argument to the read callback. - */ - data->set.in_set = va_arg(param, void *); - break; - case CURLOPT_INFILESIZE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - data->set.filesize = va_arg(param, long); - break; - case CURLOPT_INFILESIZE_LARGE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - data->set.filesize = va_arg(param, curl_off_t); - break; - case CURLOPT_LOW_SPEED_LIMIT: - /* - * The low speed limit that if transfers are below this for - * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. - */ - data->set.low_speed_limit = va_arg(param, long); - break; - case CURLOPT_MAX_SEND_SPEED_LARGE: - /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE - * bytes per second the transfer is throttled.. - */ - data->set.max_send_speed = va_arg(param, curl_off_t); - break; - case CURLOPT_MAX_RECV_SPEED_LARGE: - /* - * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per - * second the transfer is throttled.. - */ - data->set.max_recv_speed = va_arg(param, curl_off_t); - break; - case CURLOPT_LOW_SPEED_TIME: - /* - * The low speed time that if transfers are below the set - * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. - */ - data->set.low_speed_time = va_arg(param, long); - break; - case CURLOPT_URL: - /* - * The URL to fetch. - */ - if(data->change.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - result = setstropt(&data->set.str[STRING_SET_URL], - va_arg(param, char *)); - data->change.url = data->set.str[STRING_SET_URL]; - break; - case CURLOPT_PORT: - /* - * The port number to use when getting the URL - */ - data->set.use_port = va_arg(param, long); - break; - case CURLOPT_TIMEOUT: - /* - * The maximum time you allow curl to use for a single transfer - * operation. - */ - data->set.timeout = va_arg(param, long) * 1000L; - break; - - case CURLOPT_TIMEOUT_MS: - data->set.timeout = va_arg(param, long); - break; - - case CURLOPT_CONNECTTIMEOUT: - /* - * The maximum time you allow curl to use to connect. - */ - data->set.connecttimeout = va_arg(param, long) * 1000L; - break; - - case CURLOPT_CONNECTTIMEOUT_MS: - data->set.connecttimeout = va_arg(param, long); - break; - - case CURLOPT_ACCEPTTIMEOUT_MS: - /* - * The maximum time you allow curl to wait for server connect - */ - data->set.accepttimeout = va_arg(param, long); - break; - - case CURLOPT_USERPWD: - /* - * user:password to use in the operation - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_USERNAME], - &data->set.str[STRING_PASSWORD]); - break; - - case CURLOPT_USERNAME: - /* - * authentication user name to use in the operation - */ - result = setstropt(&data->set.str[STRING_USERNAME], - va_arg(param, char *)); - break; - - case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ - result = setstropt(&data->set.str[STRING_PASSWORD], - va_arg(param, char *)); - break; - - case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ - result = setstropt(&data->set.str[STRING_OPTIONS], - va_arg(param, char *)); - break; - - case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ - result = setstropt(&data->set.str[STRING_BEARER], - va_arg(param, char *)); - break; - - case CURLOPT_POSTQUOTE: - /* - * List of RAW FTP commands to use after a transfer - */ - data->set.postquote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - data->set.quote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_RESOLVE: - /* - * List of NAME:[address] names to populate the DNS cache with - * Prefix the NAME with dash (-) to _remove_ the name from the cache. - * - * Names added with this API will remain in the cache until explicitly - * removed or the handle is cleaned up. - * - * This API can remove any name from the DNS cache, but only entries - * that aren't actually in use right now will be pruned immediately. - */ - data->set.resolve = va_arg(param, struct curl_slist *); - data->change.resolve = data->set.resolve; - break; - case CURLOPT_PROGRESSFUNCTION: - /* - * Progress callback function - */ - data->set.fprogress = va_arg(param, curl_progress_callback); - if(data->set.fprogress) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - break; - - case CURLOPT_XFERINFOFUNCTION: - /* - * Transfer info callback function - */ - data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); - if(data->set.fxferinfo) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - - break; - - case CURLOPT_PROGRESSDATA: - /* - * Custom client data to pass to the progress callback - */ - data->set.progress_client = va_arg(param, void *); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: - /* - * user:password needed to use the proxy - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_PROXYUSERNAME], - &data->set.str[STRING_PROXYPASSWORD]); - break; - case CURLOPT_PROXYUSERNAME: - /* - * authentication user name to use in the operation - */ - result = setstropt(&data->set.str[STRING_PROXYUSERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PROXYPASSWORD: - /* - * authentication password to use in the operation - */ - result = setstropt(&data->set.str[STRING_PROXYPASSWORD], - va_arg(param, char *)); - break; - case CURLOPT_NOPROXY: - /* - * proxy exception list - */ - result = setstropt(&data->set.str[STRING_NOPROXY], - va_arg(param, char *)); - break; -#endif - - case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ - result = setstropt(&data->set.str[STRING_SET_RANGE], - va_arg(param, char *)); - break; - case CURLOPT_RESUME_FROM: - /* - * Resume transfer at the given file position - */ - data->set.set_resume_from = va_arg(param, long); - break; - case CURLOPT_RESUME_FROM_LARGE: - /* - * Resume transfer at the given file position - */ - data->set.set_resume_from = va_arg(param, curl_off_t); - break; - case CURLOPT_DEBUGFUNCTION: - /* - * stderr write callback. - */ - data->set.fdebug = va_arg(param, curl_debug_callback); - /* - * if the callback provided is NULL, it'll use the default callback - */ - break; - case CURLOPT_DEBUGDATA: - /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. - */ - data->set.debugdata = va_arg(param, void *); - break; - case CURLOPT_STDERR: - /* - * Set to a FILE * that should receive all error writes. This - * defaults to stderr for normal operations. - */ - data->set.err = va_arg(param, FILE *); - if(!data->set.err) - data->set.err = stderr; - break; - case CURLOPT_HEADERFUNCTION: - /* - * Set header write callback - */ - data->set.fwrite_header = va_arg(param, curl_write_callback); - break; - case CURLOPT_WRITEFUNCTION: - /* - * Set data write callback - */ - data->set.fwrite_func = va_arg(param, curl_write_callback); - if(!data->set.fwrite_func) { - data->set.is_fwrite_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fwrite_func = (curl_write_callback)fwrite; - } - else - data->set.is_fwrite_set = 1; - break; - case CURLOPT_READFUNCTION: - /* - * Read data callback - */ - data->set.fread_func_set = va_arg(param, curl_read_callback); - if(!data->set.fread_func_set) { - data->set.is_fread_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fread_func_set = (curl_read_callback)fread; - } - else - data->set.is_fread_set = 1; - break; - case CURLOPT_SEEKFUNCTION: - /* - * Seek callback. Might be NULL. - */ - data->set.seek_func = va_arg(param, curl_seek_callback); - break; - case CURLOPT_SEEKDATA: - /* - * Seek control callback. Might be NULL. - */ - data->set.seek_client = va_arg(param, void *); - break; - case CURLOPT_CONV_FROM_NETWORK_FUNCTION: - /* - * "Convert from network encoding" callback - */ - data->set.convfromnetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_TO_NETWORK_FUNCTION: - /* - * "Convert to network encoding" callback - */ - data->set.convtonetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_FROM_UTF8_FUNCTION: - /* - * "Convert from UTF-8 encoding" callback - */ - data->set.convfromutf8 = va_arg(param, curl_conv_callback); - break; - case CURLOPT_IOCTLFUNCTION: - /* - * I/O control callback. Might be NULL. - */ - data->set.ioctl_func = va_arg(param, curl_ioctl_callback); - break; - case CURLOPT_IOCTLDATA: - /* - * I/O control data pointer. Might be NULL. - */ - data->set.ioctl_client = va_arg(param, void *); - break; - case CURLOPT_SSLCERT: - /* - * String that holds file name of the SSL certificate to use - */ - result = setstropt(&data->set.str[STRING_CERT_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLCERT: - /* - * String that holds file name of the SSL certificate to use for proxy - */ - result = setstropt(&data->set.str[STRING_CERT_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - result = setstropt(&data->set.str[STRING_CERT_TYPE_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use for proxy - */ - result = setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_SSLKEY: - /* - * String that holds file name of the SSL key to use - */ - result = setstropt(&data->set.str[STRING_KEY_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLKEY: - /* - * String that holds file name of the SSL key to use for proxy - */ - result = setstropt(&data->set.str[STRING_KEY_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - result = setstropt(&data->set.str[STRING_KEY_TYPE_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use for proxy - */ - result = setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - result = setstropt(&data->set.str[STRING_KEY_PASSWD_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_KEYPASSWD: - /* - * String that holds the SSL private key password for proxy. - */ - result = setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - argptr = va_arg(param, char *); - if(argptr && argptr[0]) - result = Curl_ssl_set_engine(data, argptr); - break; - - case CURLOPT_SSLENGINE_DEFAULT: - /* - * flag to set engine as default. - */ - result = Curl_ssl_set_engine_default(data); - break; - case CURLOPT_CRLF: - /* - * Kludgy option to enable CRLF conversions. Subject for removal. - */ - data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - result = setstropt(&data->set.str[STRING_DEVICE], - va_arg(param, char *)); - break; - case CURLOPT_LOCALPORT: - /* - * Set what local port to bind the socket to when performing an operation. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localport = curlx_sltous(arg); - break; - case CURLOPT_LOCALPORTRANGE: - /* - * Set number of local ports to try, starting with CURLOPT_LOCALPORT. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localportrange = curlx_sltosi(arg); - break; - case CURLOPT_KRBLEVEL: - /* - * A string that defines the kerberos security level. - */ - result = setstropt(&data->set.str[STRING_KRB_LEVEL], - va_arg(param, char *)); - data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; - break; - case CURLOPT_GSSAPI_DELEGATION: - /* - * GSS-API credential delegation - */ - data->set.gssapi_delegation = va_arg(param, long); - break; - case CURLOPT_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying. - */ - data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - case CURLOPT_PROXY_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying for proxy. - */ - data->set.proxy_ssl.primary.verifypeer = - (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate - */ - arg = va_arg(param, long); - - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. We thus ban - 1 as a sensible input and we warn about its use. Then we only have the - 2 action internally stored as TRUE. */ - - if(1 == arg) { - failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE; - break; - case CURLOPT_PROXY_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate for proxy - */ - arg = va_arg(param, long); - - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. We thus ban - 1 as a sensible input and we warn about its use. Then we only have the - 2 action internally stored as TRUE. */ - - if(1 == arg) { - failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE; - break; - case CURLOPT_SSL_VERIFYSTATUS: - /* - * Enable certificate status verifying. - */ - if(!Curl_ssl_cert_status_request()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - case CURLOPT_SSL_CTX_FUNCTION: - /* - * Set a SSL_CTX callback - */ -#ifdef USE_SSL - if(Curl_ssl->have_ssl_ctx) - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_SSL_CTX_DATA: - /* - * Set a SSL_CTX callback parameter pointer - */ -#ifdef USE_SSL - if(Curl_ssl->have_ssl_ctx) - data->set.ssl.fsslctxp = va_arg(param, void *); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_SSL_FALSESTART: - /* - * Enable TLS false start. - */ - if(!Curl_ssl_false_start()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_CERTINFO: -#ifdef USE_SSL - if(Curl_ssl->have_certinfo) - data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl->have_pinnedpubkey) - result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_PROXY_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl->have_pinnedpubkey) - result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_CAINFO: - /* - * Set CA info for SSL connection. Specify file name of the CA certificate - */ - result = setstropt(&data->set.str[STRING_SSL_CAFILE_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_CAINFO: - /* - * Set CA info SSL connection for proxy. Specify file name of the - * CA certificate - */ - result = setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_CAPATH: - /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl->have_ca_path) - /* This does not work on windows. */ - result = setstropt(&data->set.str[STRING_SSL_CAPATH_ORIG], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_PROXY_CAPATH: - /* - * Set CA path info for SSL connection proxy. Specify directory name of the - * CA certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl->have_ca_path) - /* This does not work on windows. */ - result = setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_CRLFILE: - /* - * Set CRL file info for SSL connection. Specify file name of the CRL - * to check certificates revocation - */ - result = setstropt(&data->set.str[STRING_SSL_CRLFILE_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_CRLFILE: - /* - * Set CRL file info for SSL connection for proxy. Specify file name of the - * CRL to check certificates revocation - */ - result = setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG], - va_arg(param, char *)); - break; - case CURLOPT_TELNETOPTIONS: - /* - * Set a linked list of telnet options - */ - data->set.telnet_options = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_BUFFERSIZE: - /* - * The application kindly asks for a differently sized receive buffer. - * If it seems reasonable, we'll use it. - */ - arg = va_arg(param, long); - - if(arg > READBUFFER_MAX) - arg = READBUFFER_MAX; - else if(arg < 1) - arg = READBUFFER_SIZE; - else if(arg < READBUFFER_MIN) - arg = READBUFFER_MIN; - - /* Resize if new size */ - if(arg != data->set.buffer_size) { - char *newbuff = realloc(data->state.buffer, arg + 1); - if(!newbuff) { - DEBUGF(fprintf(stderr, "Error: realloc of buffer failed\n")); - result = CURLE_OUT_OF_MEMORY; - } - else - data->state.buffer = newbuff; - } - data->set.buffer_size = arg; - - break; - - case CURLOPT_NOSIGNAL: - /* - * The application asks not to set any signal() or alarm() handlers, - * even when using a timeout. - */ - data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_SHARE: - { - struct Curl_share *set; - set = va_arg(param, struct Curl_share *); - - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - if(data->dns.hostcachetype == HCACHE_SHARED) { - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - - if(data->share->sslsession == data->state.session) - data->state.session = NULL; - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; - } - - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - - if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) { - /* use shared host cache */ - data->dns.hostcache = &data->share->hostcache; - data->dns.hostcachetype = HCACHE_SHARED; - } -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ - if(data->share->sslsession) { - data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; - data->state.session = data->share->sslsession; - } - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ - } - break; - - case CURLOPT_PRIVATE: - /* - * Set private data pointer. - */ - data->set.private_data = va_arg(param, void *); - break; - - case CURLOPT_MAXFILESIZE: - /* - * Set the maximum size of a file to download. - */ - data->set.max_filesize = va_arg(param, long); - break; - -#ifdef USE_SSL - case CURLOPT_USE_SSL: - /* - * Make transfers attempt to use SSL/TLS. - */ - data->set.use_ssl = (curl_usessl)va_arg(param, long); - break; - - case CURLOPT_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; - data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - break; - - case CURLOPT_PROXY_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.proxy_ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; - data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - break; - -#endif - case CURLOPT_FTPSSLAUTH: - /* - * Set a specific auth for FTP-SSL transfers. - */ - data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long); - break; - - case CURLOPT_IPRESOLVE: - data->set.ipver = va_arg(param, long); - break; - - case CURLOPT_MAXFILESIZE_LARGE: - /* - * Set the maximum size of a file to download. - */ - data->set.max_filesize = va_arg(param, curl_off_t); - break; - - case CURLOPT_TCP_NODELAY: - /* - * Enable or disable TCP_NODELAY, which will disable/enable the Nagle - * algorithm - */ - data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_ACCOUNT: - result = setstropt(&data->set.str[STRING_FTP_ACCOUNT], - va_arg(param, char *)); - break; - - case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_CONNECT_ONLY: - /* - * No data transfer, set up connection and let application use the socket - */ - data->set.connect_only = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], - va_arg(param, char *)); - break; - - case CURLOPT_SOCKOPTFUNCTION: - /* - * socket callback function: called after socket() but before connect() - */ - data->set.fsockopt = va_arg(param, curl_sockopt_callback); - break; - - case CURLOPT_SOCKOPTDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.sockopt_client = va_arg(param, void *); - break; - - case CURLOPT_OPENSOCKETFUNCTION: - /* - * open/create socket callback function: called instead of socket(), - * before connect() - */ - data->set.fopensocket = va_arg(param, curl_opensocket_callback); - break; - - case CURLOPT_OPENSOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.opensocket_client = va_arg(param, void *); - break; - - case CURLOPT_CLOSESOCKETFUNCTION: - /* - * close socket callback function: called instead of close() - * when shutting down a connection - */ - data->set.fclosesocket = va_arg(param, curl_closesocket_callback); - break; - - case CURLOPT_CLOSESOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.closesocket_client = va_arg(param, void *); - break; - - case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? - TRUE : FALSE; - data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; - break; - -#ifdef USE_LIBSSH2 - /* we only include SSH options if explicitly built to support SSH */ - case CURLOPT_SSH_AUTH_TYPES: - data->set.ssh_auth_types = va_arg(param, long); - break; - - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa.pub file - */ - result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_PRIVATE_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa file - */ - result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], - va_arg(param, char *)); - break; - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - /* - * Option to allow for the MD5 of the host public key to be checked - * for validation purposes. - */ - result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], - va_arg(param, char *)); - break; -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the file name to read known hosts from. - */ - result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_KEYFUNCTION: - /* setting to NULL is fine since the ssh.c functions themselves will - then rever to use the internal default */ - data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); - break; - - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - data->set.ssh_keyfunc_userp = va_arg(param, void *); - break; -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - -#endif /* USE_LIBSSH2 */ - - case CURLOPT_HTTP_TRANSFER_DECODING: - /* - * disable libcurl transfer encoding is used - */ - data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_HTTP_CONTENT_DECODING: - /* - * raw data passed to the application when content encoding is used - */ - data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_NEW_FILE_PERMS: - /* - * Uses these permissions instead of 0644 - */ - data->set.new_file_perms = va_arg(param, long); - break; - - case CURLOPT_NEW_DIRECTORY_PERMS: - /* - * Uses these permissions instead of 0755 - */ - data->set.new_directory_perms = va_arg(param, long); - break; - - case CURLOPT_ADDRESS_SCOPE: - /* - * We always get longs when passed plain numericals, but for this value we - * know that an unsigned int will always hold the value so we blindly - * typecast to this type - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 0xf)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.scope_id = curlx_sltoui(arg); - break; - - case CURLOPT_PROTOCOLS: - /* set the bitmask for the protocols that are allowed to be used for the - transfer, which thus helps the app which takes URLs from users or other - external inputs and want to restrict what protocol(s) to deal - with. Defaults to CURLPROTO_ALL. */ - data->set.allowed_protocols = va_arg(param, long); - break; - - case CURLOPT_REDIR_PROTOCOLS: - /* set the bitmask for the protocols that libcurl is allowed to follow to, - as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ - data->set.redir_protocols = va_arg(param, long); - break; - - case CURLOPT_DEFAULT_PROTOCOL: - /* Set the protocol to use when the URL doesn't include any protocol */ - result = setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_FROM: - /* Set the SMTP mail originator */ - result = setstropt(&data->set.str[STRING_MAIL_FROM], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_AUTH: - /* Set the SMTP auth originator */ - result = setstropt(&data->set.str[STRING_MAIL_AUTH], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_RCPT: - /* Set the list of mail recipients */ - data->set.mail_rcpt = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_SASL_IR: - /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_RTSP_REQUEST: - { - /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? - */ - long curl_rtspreq = va_arg(param, long); - Curl_RtspReq rtspreq = RTSPREQ_NONE; - switch(curl_rtspreq) { - case CURL_RTSPREQ_OPTIONS: - rtspreq = RTSPREQ_OPTIONS; - break; - - case CURL_RTSPREQ_DESCRIBE: - rtspreq = RTSPREQ_DESCRIBE; - break; - - case CURL_RTSPREQ_ANNOUNCE: - rtspreq = RTSPREQ_ANNOUNCE; - break; - - case CURL_RTSPREQ_SETUP: - rtspreq = RTSPREQ_SETUP; - break; - - case CURL_RTSPREQ_PLAY: - rtspreq = RTSPREQ_PLAY; - break; - - case CURL_RTSPREQ_PAUSE: - rtspreq = RTSPREQ_PAUSE; - break; - - case CURL_RTSPREQ_TEARDOWN: - rtspreq = RTSPREQ_TEARDOWN; - break; - - case CURL_RTSPREQ_GET_PARAMETER: - rtspreq = RTSPREQ_GET_PARAMETER; - break; - - case CURL_RTSPREQ_SET_PARAMETER: - rtspreq = RTSPREQ_SET_PARAMETER; - break; - - case CURL_RTSPREQ_RECORD: - rtspreq = RTSPREQ_RECORD; - break; - - case CURL_RTSPREQ_RECEIVE: - rtspreq = RTSPREQ_RECEIVE; - break; - default: - rtspreq = RTSPREQ_NONE; - } - - data->set.rtspreq = rtspreq; - break; - } - - - case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * resuming a previously established RTSP session - */ - result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_STREAM_URI: - /* - * Set the Stream URI for the RTSP request. Unless the request is - * for generic server options, the application will need to set this. - */ - result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_TRANSPORT: - /* - * The content of the Transport: header for the RTSP request - */ - result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_CLIENT_CSEQ: - /* - * Set the CSEQ number to issue for the next RTSP request. Useful if the - * application is resuming a previously broken connection. The CSEQ - * will increment from this new number henceforth. - */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - /* Same as the above, but for server-initiated requests */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_INTERLEAVEDATA: - data->set.rtp_out = va_arg(param, void *); - break; - case CURLOPT_INTERLEAVEFUNCTION: - /* Set the user defined RTP write function */ - data->set.fwrite_rtp = va_arg(param, curl_write_callback); - break; - - case CURLOPT_WILDCARDMATCH: - data->set.wildcardmatch = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_CHUNK_BGN_FUNCTION: - data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); - break; - case CURLOPT_CHUNK_END_FUNCTION: - data->set.chunk_end = va_arg(param, curl_chunk_end_callback); - break; - case CURLOPT_FNMATCH_FUNCTION: - data->set.fnmatch = va_arg(param, curl_fnmatch_callback); - break; - case CURLOPT_CHUNK_DATA: - data->wildcard.customptr = va_arg(param, void *); - break; - case CURLOPT_FNMATCH_DATA: - data->set.fnmatch_data = va_arg(param, void *); - break; -#ifdef USE_TLS_SRP - case CURLOPT_TLSAUTH_USERNAME: - result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_ORIG], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_ORIG] && !data->set.ssl.authtype) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_PROXY_TLSAUTH_USERNAME: - result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.authtype) - data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_TLSAUTH_PASSWORD: - result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_ORIG], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_ORIG] && !data->set.ssl.authtype) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_PROXY_TLSAUTH_PASSWORD: - result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.authtype) - data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(!argptr || - strncasecompare(argptr, "SRP", strlen("SRP"))) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; - else - data->set.ssl.authtype = CURL_TLSAUTH_NONE; - break; - case CURLOPT_PROXY_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(!argptr || - strncasecompare(argptr, "SRP", strlen("SRP"))) - data->set.proxy_ssl.authtype = CURL_TLSAUTH_SRP; - else - data->set.proxy_ssl.authtype = CURL_TLSAUTH_NONE; - break; -#endif - case CURLOPT_DNS_SERVERS: - result = Curl_set_dns_servers(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_INTERFACE: - result = Curl_set_dns_interface(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_LOCAL_IP4: - result = Curl_set_dns_local_ip4(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_LOCAL_IP6: - result = Curl_set_dns_local_ip6(data, va_arg(param, char *)); - break; - - case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_TCP_KEEPIDLE: - data->set.tcp_keepidle = va_arg(param, long); - break; - case CURLOPT_TCP_KEEPINTVL: - data->set.tcp_keepintvl = va_arg(param, long); - break; - case CURLOPT_TCP_FASTOPEN: -#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) - data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE; -#else - result = CURLE_NOT_BUILT_IN; -#endif - break; - case CURLOPT_SSL_ENABLE_NPN: - data->set.ssl_enable_npn = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - data->set.abstract_unix_socket = FALSE; - result = setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); - break; - case CURLOPT_ABSTRACT_UNIX_SOCKET: - data->set.abstract_unix_socket = TRUE; - result = setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); - break; -#endif - - case CURLOPT_PATH_AS_IS: - data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_PIPEWAIT: - data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_STREAM_WEIGHT: -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else - arg = va_arg(param, long); - if((arg >= 1) && (arg <= 256)) - data->set.stream_weight = (int)arg; - break; -#endif - case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: - { -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) { - if(data->set.stream_depends_on) { - Curl_http2_remove_child(data->set.stream_depends_on, data); - } - Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); - } - break; -#endif - } - case CURLOPT_CONNECT_TO: - data->set.connect_to = va_arg(param, struct curl_slist *); - break; - case CURLOPT_SUPPRESS_CONNECT_HEADERS: - data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_SSH_COMPRESSION: - data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE; - break; - default: - /* unknown tag and its companion, just ignore: */ - result = CURLE_UNKNOWN_OPTION; - break; - } - - return result; -} - #ifdef USE_RECV_BEFORE_SEND_WORKAROUND static void conn_reset_postponed_data(struct connectdata *conn, int num) { @@ -3081,6 +714,9 @@ static void conn_free(struct connectdata *conn) Curl_safefree(conn->unix_domain_socket); #endif +#ifdef USE_SSL + Curl_safefree(conn->ssl_extra); +#endif free(conn); /* free all the connection oriented data */ } @@ -3135,7 +771,7 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) /* unlink ourselves! */ infof(data, "Closing connection %ld\n", conn->connection_id); - Curl_conncache_remove_conn(data->state.conn_cache, conn); + Curl_conncache_remove_conn(conn, TRUE); free_fixed_hostname(&conn->host); free_fixed_hostname(&conn->conn_to_host); @@ -3289,58 +925,6 @@ static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) } } -/* - * This function finds the connection in the connection - * cache that has been unused for the longest time. - * - * Returns the pointer to the oldest idle connection, or NULL if none was - * found. - */ -struct connectdata * -Curl_oldest_idle_connection(struct Curl_easy *data) -{ - struct conncache *bc = data->state.conn_cache; - struct curl_hash_iterator iter; - struct curl_llist_element *curr; - struct curl_hash_element *he; - time_t highscore =- 1; - time_t score; - struct curltime now; - struct connectdata *conn_candidate = NULL; - struct connectbundle *bundle; - - now = Curl_tvnow(); - - Curl_hash_start_iterate(&bc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct connectdata *conn; - - bundle = he->ptr; - - curr = bundle->conn_list.head; - while(curr) { - conn = curr->ptr; - - if(!conn->inuse) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_tvdiff(now, conn->now); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - } - } - curr = curr->next; - } - - he = Curl_hash_next_element(&iter); - } - - return conn_candidate; -} - static bool proxy_info_matches(const struct proxy_info* data, const struct proxy_info* needle) @@ -3353,56 +937,17 @@ proxy_info_matches(const struct proxy_info* data, return FALSE; } - /* - * This function finds the connection in the connection - * bundle that has been unused for the longest time. + * This function checks if the given connection is dead and extracts it from + * the connection cache if so. * - * Returns the pointer to the oldest idle connection, or NULL if none was - * found. - */ -static struct connectdata * -find_oldest_idle_connection_in_bundle(struct Curl_easy *data, - struct connectbundle *bundle) -{ - struct curl_llist_element *curr; - time_t highscore = -1; - time_t score; - struct curltime now; - struct connectdata *conn_candidate = NULL; - struct connectdata *conn; - - (void)data; - - now = Curl_tvnow(); - - curr = bundle->conn_list.head; - while(curr) { - conn = curr->ptr; - - if(!conn->inuse) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_tvdiff(now, conn->now); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - } - } - curr = curr->next; - } - - return conn_candidate; -} - -/* - * This function checks if given connection is dead and disconnects if so. - * (That also removes it from the connection cache.) + * When this is called as a Curl_conncache_foreach() callback, the connection + * cache lock is held! * - * Returns TRUE if the connection actually was dead and disconnected. + * Returns TRUE if the connection was dead and extracted. */ -static bool disconnect_if_dead(struct connectdata *conn, - struct Curl_easy *data) +static bool extract_if_dead(struct connectdata *conn, + struct Curl_easy *data) { size_t pipeLen = conn->send_pipe.size + conn->recv_pipe.size; if(!pipeLen && !conn->inuse) { @@ -3427,25 +972,30 @@ static bool disconnect_if_dead(struct connectdata *conn, if(dead) { conn->data = data; infof(data, "Connection %ld seems to be dead!\n", conn->connection_id); - - /* disconnect resources */ - Curl_disconnect(conn, /* dead_connection */TRUE); + Curl_conncache_remove_conn(conn, FALSE); return TRUE; } } return FALSE; } +struct prunedead { + struct Curl_easy *data; + struct connectdata *extracted; +}; + /* - * Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach() + * Wrapper to use extract_if_dead() function in Curl_conncache_foreach() * - * Returns always 0. */ -static int call_disconnect_if_dead(struct connectdata *conn, - void *param) +static int call_extract_if_dead(struct connectdata *conn, void *param) { - struct Curl_easy* data = (struct Curl_easy*)param; - disconnect_if_dead(conn, data); + struct prunedead *p = (struct prunedead *)param; + if(extract_if_dead(conn, p->data)) { + /* stop the iteration here, pass back the connection that was extracted */ + p->extracted = conn; + return 1; + } return 0; /* continue iteration */ } @@ -3456,12 +1006,18 @@ static int call_disconnect_if_dead(struct connectdata *conn, */ static void prune_dead_connections(struct Curl_easy *data) { - struct curltime now = Curl_tvnow(); - time_t elapsed = Curl_tvdiff(now, data->state.conn_cache->last_cleanup); + struct curltime now = Curl_now(); + time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup); if(elapsed >= 1000L) { - Curl_conncache_foreach(data->state.conn_cache, data, - call_disconnect_if_dead); + struct prunedead prune; + prune.data = data; + prune.extracted = NULL; + while(Curl_conncache_foreach(data, data->state.conn_cache, &prune, + call_extract_if_dead)) { + /* disconnect it */ + (void)Curl_disconnect(prune.extracted, /* dead_connection */TRUE); + } data->state.conn_cache->last_cleanup = now; } } @@ -3516,8 +1072,8 @@ ConnectionExists(struct Curl_easy *data, Curl_pipeline_site_blacklisted(data, needle)) canpipe &= ~ CURLPIPE_HTTP1; - /* Look up the bundle with all the connections to this - particular host */ + /* Look up the bundle with all the connections to this particular host. + Locks the connection cache, beware of early returns! */ bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache); if(bundle) { /* Max pipe length is zero (unlimited) for multiplexed connections */ @@ -3540,6 +1096,7 @@ ConnectionExists(struct Curl_easy *data, if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { infof(data, "Server doesn't support multi-use yet, wait\n"); *waitpipe = TRUE; + Curl_conncache_unlock(needle); return FALSE; /* no re-use */ } @@ -3571,8 +1128,11 @@ ConnectionExists(struct Curl_easy *data, check = curr->ptr; curr = curr->next; - if(disconnect_if_dead(check, data)) + if(extract_if_dead(check, data)) { + /* disconnect it */ + (void)Curl_disconnect(check, /* dead_connection */TRUE); continue; + } pipeLen = check->send_pipe.size + check->recv_pipe.size; @@ -3703,6 +1263,11 @@ ConnectionExists(struct Curl_easy *data, already in use so we skip it */ continue; + if((check->inuse) && (check->data->multi != needle->data->multi)) + /* this could be subject for pipeline/multiplex use, but only + if they belong to the same multi handle */ + continue; + if(needle->localdev || needle->localport) { /* If we are bound to a specific local end (IP+port), we must not re-use a random other one, although if we didn't ask for a @@ -3889,9 +1454,13 @@ ConnectionExists(struct Curl_easy *data, } if(chosen) { + /* mark it as used before releasing the lock */ + chosen->inuse = TRUE; + Curl_conncache_unlock(needle); *usethis = chosen; return TRUE; /* yes, we found one to use! */ } + Curl_conncache_unlock(needle); if(foundPendingCandidate && data->set.pipewait) { infof(data, @@ -4104,7 +1673,7 @@ static bool is_ASCII_name(const char *hostname) /* * Perform any necessary IDN conversion of hostname */ -static void fix_hostname(struct connectdata *conn, struct hostname *host) +static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host) { size_t len; struct Curl_easy *data = conn->data; @@ -4144,9 +1713,11 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) /* change the name pointer to point to the encoded hostname */ host->name = host->encalloc; } - else - infof(data, "Failed to convert %s to ACE; %s\n", host->name, + else { + failf(data, "Failed to convert %s to ACE; %s\n", host->name, idn2_strerror(rc)); + return CURLE_URL_MALFORMAT; + } } #elif defined(USE_WIN32_IDN) char *ace_hostname = NULL; @@ -4156,12 +1727,24 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) /* change the name pointer to point to the encoded hostname */ host->name = host->encalloc; } - else - infof(data, "Failed to convert %s to ACE;\n", host->name); + else { + failf(data, "Failed to convert %s to ACE;\n", host->name); + return CURLE_URL_MALFORMAT; + } #else infof(data, "IDN support not present, can't parse Unicode domains\n"); #endif } + { + char *hostp; + for(hostp = host->name; *hostp; hostp++) { + if(*hostp <= 32) { + failf(data, "Host name '%s' contains bad letter", host->name); + return CURLE_URL_MALFORMAT; + } + } + } + return CURLE_OK; } /* @@ -4196,15 +1779,30 @@ static void llist_dtor(void *user, void *element) */ static struct connectdata *allocate_conn(struct Curl_easy *data) { -#ifdef USE_SSL -#define SSL_EXTRA + 4 * Curl_ssl->sizeof_ssl_backend_data - sizeof(long long) -#else -#define SSL_EXTRA 0 -#endif - struct connectdata *conn = calloc(1, sizeof(struct connectdata) + SSL_EXTRA); + struct connectdata *conn = calloc(1, sizeof(struct connectdata)); if(!conn) return NULL; +#ifdef USE_SSL + /* The SSL backend-specific data (ssl_backend_data) objects are allocated as + a separate array to ensure suitable alignment. + Note that these backend pointers can be swapped by vtls (eg ssl backend + data becomes proxy backend data). */ + { + size_t sslsize = Curl_ssl->sizeof_ssl_backend_data; + char *ssl = calloc(4, sslsize); + if(!ssl) { + free(conn); + return NULL; + } + conn->ssl_extra = ssl; + conn->ssl[0].backend = (void *)ssl; + conn->ssl[1].backend = (void *)(ssl + sslsize); + conn->proxy_ssl[0].backend = (void *)(ssl + 2 * sslsize); + conn->proxy_ssl[1].backend = (void *)(ssl + 3 * sslsize); + } +#endif + conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined already from start to avoid NULL situations and checks */ @@ -4229,7 +1827,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) connclose(conn, "Default to force-close"); /* Store creation time to help future close decision making */ - conn->created = Curl_tvnow(); + conn->created = Curl_now(); conn->data = data; /* Setup the association between this connection and the Curl_easy */ @@ -4284,23 +1882,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->ip_version = data->set.ipver; -#ifdef USE_SSL - /* - * To save on malloc()s, the SSL backend-specific data has been allocated - * at the end of the connectdata struct. - */ - { - char *p = (char *)&conn->align_data__do_not_use; - conn->ssl[0].backend = (struct ssl_backend_data *)p; - conn->ssl[1].backend = - (struct ssl_backend_data *)(p + Curl_ssl->sizeof_ssl_backend_data); - conn->proxy_ssl[0].backend = - (struct ssl_backend_data *)(p + Curl_ssl->sizeof_ssl_backend_data * 2); - conn->proxy_ssl[1].backend = - (struct ssl_backend_data *)(p + Curl_ssl->sizeof_ssl_backend_data * 3); - } -#endif - #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ defined(NTLM_WB_ENABLED) conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; @@ -4347,6 +1928,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) free(conn->master_buffer); free(conn->localdev); +#ifdef USE_SSL + free(conn->ssl_extra); +#endif free(conn); return NULL; } @@ -4442,6 +2026,14 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, ('A' <= str[0] && str[0] <= 'Z')) && \ (str[1] == ':')) + /* MSDOS/Windows style drive prefix, optionally with + * a '|' instead of ':', followed by a slash or NUL */ +#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':' || (str)[1] == '|') && \ + ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) + /* Don't mistake a drive letter for a scheme if the default protocol is file. curld --proto-default file c:/foo/bar.txt */ if(STARTS_WITH_DRIVE_PREFIX(data->change.url) && @@ -4474,63 +2066,90 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return CURLE_URL_MALFORMAT; } - if(url_has_scheme && path[0] == '/' && path[1] == '/') { - /* Allow omitted hostname (e.g. file:/<path>). This is not strictly - * speaking a valid file: URL by RFC 1738, but treating file:/<path> as - * file://localhost/<path> is similar to how other schemes treat missing - * hostnames. See RFC 1808. */ - - /* This cannot be done with strcpy() in a portable manner, since the - memory areas overlap! */ - memmove(path, path + 2, strlen(path + 2) + 1); + if(url_has_scheme && path[0] == '/' && path[1] == '/' && + path[2] == '/' && path[3] == '/') { + /* This appears to be a UNC string (usually indicating a SMB share). + * We don't do SMB in file: URLs. (TODO?) + */ + failf(data, "SMB shares are not supported in file: URLs."); + return CURLE_URL_MALFORMAT; } - /* - * we deal with file://<host>/<path> differently since it supports no - * hostname other than "localhost" and "127.0.0.1", which is unique among - * the URL protocols specified in RFC 1738 + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/<path>) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. */ - if(path[0] != '/' && !STARTS_WITH_DRIVE_PREFIX(path)) { - /* the URL includes a host name, it must match "localhost" or - "127.0.0.1" to be valid */ - char *ptr; - if(!checkprefix("localhost/", path) && - !checkprefix("127.0.0.1/", path)) { - failf(data, "Invalid file://hostname/, " - "expected localhost or 127.0.0.1 or none"); - return CURLE_URL_MALFORMAT; - } - ptr = &path[9]; /* now points to the slash after the host */ - - /* there was a host name and slash present - - RFC1738 (section 3.1, page 5) says: - - The rest of the locator consists of data specific to the scheme, - and is known as the "url-path". It supplies the details of how the - specified resource can be accessed. Note that the "/" between the - host (or port) and the url-path is NOT part of the url-path. + if(url_has_scheme && path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + char *ptr = &path[2]; - As most agents use file://localhost/foo to get '/foo' although the - slash preceding foo is a separator and not a slash for the path, - a URL as file://localhost//foo must be valid as well, to refer to - the same file with an absolute path. - */ + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or + * + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine. + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) + */ + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a host name, it must match "localhost" or + "127.0.0.1" to be valid */ + if(!checkprefix("localhost/", ptr) && + !checkprefix("127.0.0.1/", ptr)) { + failf(data, "Invalid file://hostname/, " + "expected localhost or 127.0.0.1 or none"); + return CURLE_URL_MALFORMAT; + } + ptr += 9; /* now points to the slash after the host */ + } - if('/' == ptr[1]) - /* if there was two slashes, we skip the first one as that is then - used truly as a separator */ + /* + * RFC 8089, Appendix D, Section D.1, says: + * + * > In a POSIX file system, the root of the file system is represented + * > as a directory with a zero-length name, usually written as "/"; the + * > presence of this root in a file URI can be taken as given by the + * > initial slash in the "path-absolute" rule. + * + * i.e. the first slash is part of the path. + * + * However in RFC 1738 the "/" between the host (or port) and the + * URL-path was NOT part of the URL-path. Any agent that followed the + * older spec strictly, and wanted to refer to a file with an absolute + * path, would have included a second slash. So if there are two + * slashes, swallow one. + */ + if('/' == ptr[1]) /* note: the only way ptr[0]!='/' is if ptr[1]==':' */ ptr++; - /* This cannot be made with strcpy, as the memory chunks overlap! */ + /* This cannot be done with strcpy, as the memory chunks overlap! */ memmove(path, ptr, strlen(ptr) + 1); } #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) - if(STARTS_WITH_DRIVE_PREFIX(path)) { + /* Don't allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { failf(data, "File drive letters are only accepted in MSDOS/Windows."); return CURLE_URL_MALFORMAT; } +#else + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + memmove(path, &path[1], strlen(&path[1]) + 1); + } #endif protop = "file"; /* protocol string */ @@ -5142,8 +2761,8 @@ static CURLcode parse_proxy(struct Curl_easy *data, atsign = strchr(proxyptr, '@'); if(atsign) { CURLcode result = - parse_login_details(proxyptr, atsign - proxyptr, - &proxyuser, &proxypasswd, NULL); + Curl_parse_login_details(proxyptr, atsign - proxyptr, + &proxyuser, &proxypasswd, NULL); if(result) return result; proxyptr = atsign + 1; @@ -5534,10 +3153,11 @@ static CURLcode parse_url_login(struct Curl_easy *data, /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. */ - result = parse_login_details(login, ptr - login - 1, - &userp, &passwdp, - (conn->handler->flags & PROTOPT_URLOPTIONS)? - &optionsp:NULL); + result = + Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (conn->handler->flags & PROTOPT_URLOPTIONS)? + &optionsp:NULL); if(result) goto out; @@ -5593,7 +3213,7 @@ static CURLcode parse_url_login(struct Curl_easy *data, } /* - * parse_login_details() + * Curl_parse_login_details() * * This is used to parse a login string for user name, password and options in * the following formats: @@ -5621,9 +3241,9 @@ static CURLcode parse_url_login(struct Curl_easy *data, * * Returns CURLE_OK on success. */ -static CURLcode parse_login_details(const char *login, const size_t len, - char **userp, char **passwdp, - char **optionsp) +CURLcode Curl_parse_login_details(const char *login, const size_t len, + char **userp, char **passwdp, + char **optionsp) { CURLcode result = CURLE_OK; char *ubuf = NULL; @@ -5752,7 +3372,13 @@ static CURLcode parse_remote_port(struct Curl_easy *data, portptr = strchr(conn->host.name, ']'); if(portptr) { *portptr++ = '\0'; /* zero terminate, killing the bracket */ - if(':' != *portptr) + if(*portptr) { + if (*portptr != ':') { + failf(data, "IPv6 closing bracket followed by '%c'", *portptr); + return CURLE_URL_MALFORMAT; + } + } + else portptr = NULL; /* no port number available */ } } @@ -5986,6 +3612,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, /* detect and extract RFC6874-style IPv6-addresses */ if(*hostptr == '[') { +#ifdef ENABLE_IPV6 char *ptr = ++hostptr; /* advance beyond the initial bracket */ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) ptr++; @@ -6009,6 +3636,11 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, * hostptr first, but I can't see anything wrong with that as no host * name nor a numeric can legally start with a bracket. */ +#else + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in!"); + free(host_dup); + return CURLE_NOT_BUILT_IN; +#endif } /* Get port number off server.com:1080 */ @@ -6171,7 +3803,7 @@ static CURLcode resolve_server(struct Curl_easy *data, bool *async) { CURLcode result = CURLE_OK; - time_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); /************************************************************* * Resolve the name of the server or proxy @@ -6588,13 +4220,24 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * IDN-fix the hostnames *************************************************************/ - fix_hostname(conn, &conn->host); - if(conn->bits.conn_to_host) - fix_hostname(conn, &conn->conn_to_host); - if(conn->bits.httpproxy) - fix_hostname(conn, &conn->http_proxy.host); - if(conn->bits.socksproxy) - fix_hostname(conn, &conn->socks_proxy.host); + result = fix_hostname(conn, &conn->host); + if(result) + goto out; + if(conn->bits.conn_to_host) { + result = fix_hostname(conn, &conn->conn_to_host); + if(result) + goto out; + } + if(conn->bits.httpproxy) { + result = fix_hostname(conn, &conn->http_proxy.host); + if(result) + goto out; + } + if(conn->bits.socksproxy) { + result = fix_hostname(conn, &conn->socks_proxy.host); + if(result) + goto out; + } /************************************************************* * Check whether the host and the "connect to host" are equal. @@ -6751,20 +4394,21 @@ static CURLcode create_conn(struct Curl_easy *data, else reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); - /* If we found a reusable connection, we may still want to - open a new connection if we are pipelining. */ + /* If we found a reusable connection that is now marked as in use, we may + still want to open a new connection if we are pipelining. */ if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) { size_t pipelen = conn_temp->send_pipe.size + conn_temp->recv_pipe.size; if(pipelen > 0) { infof(data, "Found connection %ld, with requests in the pipe (%zu)\n", conn_temp->connection_id, pipelen); - if(conn_temp->bundle->num_connections < max_host_connections && - data->state.conn_cache->num_connections < max_total_connections) { + if(Curl_conncache_bundle_size(conn_temp) < max_host_connections && + Curl_conncache_size(data) < max_total_connections) { /* We want a new connection anyway */ reuse = FALSE; infof(data, "We can reuse, but we want a new connection anyway\n"); + Curl_conncache_return_conn(conn_temp); } } } @@ -6776,9 +4420,10 @@ static CURLcode create_conn(struct Curl_easy *data, * just allocated before we can move along and use the previously * existing one. */ - conn_temp->inuse = TRUE; /* mark this as being in use so that no other - handle in a multi stack may nick it */ reuse_conn(conn, conn_temp); +#ifdef USE_SSL + free(conn->ssl_extra); +#endif free(conn); /* we don't need this anymore */ conn = conn_temp; *in_connect = conn; @@ -6794,7 +4439,6 @@ static CURLcode create_conn(struct Curl_easy *data, /* We have decided that we want a new connection. However, we may not be able to do that if we have reached the limit of how many connections we are allowed to open. */ - struct connectbundle *bundle = NULL; if(conn->handler->flags & PROTOPT_ALPN_NPN) { /* The protocol wants it, so set the bits if enabled in the easy handle @@ -6809,35 +4453,42 @@ static CURLcode create_conn(struct Curl_easy *data, /* There is a connection that *might* become usable for pipelining "soon", and we wait for that */ connections_available = FALSE; - else - bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); - - if(max_host_connections > 0 && bundle && - (bundle->num_connections >= max_host_connections)) { - struct connectdata *conn_candidate; - - /* The bundle is full. Let's see if we can kill a connection. */ - conn_candidate = find_oldest_idle_connection_in_bundle(data, bundle); - - if(conn_candidate) { - /* Set the connection's owner correctly, then kill it */ - conn_candidate->data = data; - (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); - } - else { - infof(data, "No more connections allowed to host: %d\n", - max_host_connections); - connections_available = FALSE; + else { + /* this gets a lock on the conncache */ + struct connectbundle *bundle = + Curl_conncache_find_bundle(conn, data->state.conn_cache); + + if(max_host_connections > 0 && bundle && + (bundle->num_connections >= max_host_connections)) { + struct connectdata *conn_candidate; + + /* The bundle is full. Extract the oldest connection. */ + conn_candidate = Curl_conncache_extract_bundle(data, bundle); + Curl_conncache_unlock(conn); + + if(conn_candidate) { + /* Set the connection's owner correctly, then kill it */ + conn_candidate->data = data; + (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); + } + else { + infof(data, "No more connections allowed to host: %d\n", + max_host_connections); + connections_available = FALSE; + } } + else + Curl_conncache_unlock(conn); + } if(connections_available && (max_total_connections > 0) && - (data->state.conn_cache->num_connections >= max_total_connections)) { + (Curl_conncache_size(data) >= max_total_connections)) { struct connectdata *conn_candidate; /* The cache is full. Let's see if we can kill a connection. */ - conn_candidate = Curl_oldest_idle_connection(data); + conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) { /* Set the connection's owner correctly, then kill it */ @@ -6860,6 +4511,9 @@ static CURLcode create_conn(struct Curl_easy *data, goto out; } else { + /* Mark the connection as used, before we add it */ + conn->inuse = TRUE; + /* * This is a brand new connection, so let's store it in the connection * cache of ours! @@ -6887,9 +4541,6 @@ static CURLcode create_conn(struct Curl_easy *data, #endif } - /* Mark the connection as used */ - conn->inuse = TRUE; - /* Setup and init stuff before DO starts, in preparing for the transfer. */ Curl_init_do(data, conn); @@ -6972,7 +4623,7 @@ CURLcode Curl_setup_conn(struct connectdata *conn, /* set start time here for timeout purposes in the connect procedure, it is later set again for the progress meter purpose */ - conn->now = Curl_tvnow(); + conn->now = Curl_now(); if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; @@ -6989,22 +4640,8 @@ CURLcode Curl_setup_conn(struct connectdata *conn, Curl_verboseconnect(conn); } - conn->now = Curl_tvnow(); /* time this *after* the connect is done, we - set this here perhaps a second time */ - -#ifdef __EMX__ - /* - * This check is quite a hack. We're calling _fsetmode to fix the problem - * with fwrite converting newline characters (you get mangled text files, - * and corrupted binary files when you download to stdout and redirect it to - * a file). - */ - - if((data->set.out)->_handle == NULL) { - _fsetmode(stdout, "b"); - } -#endif - + conn->now = Curl_now(); /* time this *after* the connect is done, we set + this here perhaps a second time */ return result; } @@ -7062,13 +4699,17 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) { struct SingleRequest *k = &data->req; - if(conn) - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to - * use */ + conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to + use */ data->state.done = FALSE; /* *_done() is not called yet */ data->state.expect100header = FALSE; + /* if the protocol used doesn't support wildcards, switch it off */ + if(data->state.wildcardmatch && + !(conn->handler->flags & PROTOPT_WILDCARD)) + data->state.wildcardmatch = FALSE; + if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->set.httpreq = HTTPREQ_HEAD; @@ -7080,7 +4721,7 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) HTTP. */ data->set.httpreq = HTTPREQ_GET; - k->start = Curl_tvnow(); /* start time */ + k->start = Curl_now(); /* start time */ k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index f13c8e6..a70bd54 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,15 +23,17 @@ ***************************************************************************/ #include "curl_setup.h" +#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE +#define READBUFFER_MAX CURL_MAX_READ_SIZE +#define READBUFFER_MIN 1024 + /* * Prototypes for library-wide functions provided by url.c */ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_open(struct Curl_easy **curl); -CURLcode Curl_init_userdefined(struct UserDefined *set); -CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option, - va_list arg); +CURLcode Curl_init_userdefined(struct Curl_easy *data); CURLcode Curl_dupset(struct Curl_easy * dst, struct Curl_easy * src); void Curl_freeset(struct Curl_easy * data); CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */ @@ -51,14 +53,14 @@ int Curl_protocol_getsock(struct connectdata *conn, int Curl_doing_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); - +CURLcode Curl_parse_login_details(const char *login, const size_t len, + char **userptr, char **passwdptr, + char **optionsptr); bool Curl_isPipeliningEnabled(const struct Curl_easy *handle); CURLcode Curl_addHandleToPipeline(struct Curl_easy *handle, struct curl_llist *pipeline); int Curl_removeHandleFromPipeline(struct Curl_easy *handle, struct curl_llist *pipeline); -struct connectdata * -Curl_oldest_idle_connection(struct Curl_easy *data); /* remove the specified connection from all (possible) pipelines and related queues */ void Curl_getoff_all_pipelines(struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 66e4596..5c04ad1 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -85,17 +85,12 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif #include "timeval.h" -#ifdef HAVE_ZLIB_H -#include <zlib.h> /* for content-encoding */ -#ifdef __SYMBIAN32__ -/* zlib pollutes the namespace with this definition */ -#undef WIN32 -#endif -#endif - #include <curl/curl.h> #include "http_chunks.h" /* for the structs and enum stuff */ @@ -286,6 +281,7 @@ struct digestdata { char *qop; char *algorithm; int nc; /* nounce count */ + bool userhash; #endif }; @@ -463,16 +459,6 @@ struct hostname { #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) -#ifdef HAVE_LIBZ -typedef enum { - ZLIB_UNINIT, /* uninitialized */ - ZLIB_INIT, /* initialized */ - ZLIB_GZIP_HEADER, /* reading gzip header */ - ZLIB_GZIP_INFLATING, /* inflating gzip stream */ - ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ -} zlibInitState; -#endif - #ifdef CURLRES_ASYNCH struct Curl_async { char *hostname; @@ -560,18 +546,8 @@ struct SingleRequest { enum expect100 exp100; /* expect 100 continue state */ enum upgrade101 upgr101; /* 101 upgrade state */ - int auto_decoding; /* What content encoding. sec 3.5, RFC2616. */ - -#define IDENTITY 0 /* No encoding */ -#define DEFLATE 1 /* zlib deflate [RFC 1950 & 1951] */ -#define GZIP 2 /* gzip algorithm [RFC 1952] */ - -#ifdef HAVE_LIBZ - zlibInitState zlib_init; /* possible zlib init state; - undefined if Content-Encoding header. */ - z_stream z; /* State structure for zlib. */ -#endif - + struct contenc_writer_s *writer_stack; /* Content unencoding stack. */ + /* See sec 3.5, RFC2616. */ time_t timeofdoc; long bodywrites; @@ -719,6 +695,7 @@ struct Curl_handler { #define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a HTTP proxy as HTTP proxies may know this protocol and act as a gateway */ +#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ #define CONNCHECK_NONE 0 /* No checks */ #define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ @@ -778,6 +755,7 @@ struct http_connect_state { TUNNEL_CONNECT, /* CONNECT has been sent off */ TUNNEL_COMPLETE /* CONNECT response received completely */ } tunnel_state; + bool close_connection; }; /* @@ -801,9 +779,10 @@ struct connectdata { void *closesocket_client; bool inuse; /* This is a marker for the connection cache logic. If this is - TRUE this handle is being used by an easy handle and cannot - be used by any other easy handle without careful - consideration (== only for pipelining). */ + TRUE this handle is being used by one or more easy handles + and can only used by any other easy handle without careful + consideration (== only for pipelining/multiplexing) and it + cannot be used by another multi handle! */ /**** Fields set when inited and not modified again */ long connection_id; /* Contains a unique number to make it easier to @@ -886,6 +865,9 @@ struct connectdata { #endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ struct ssl_connect_data proxy_ssl[2]; /* this is for proxy ssl-stuff */ +#ifdef USE_SSL + void *ssl_extra; /* separately allocated backend-specific data */ +#endif struct ssl_primary_config ssl_config; struct ssl_primary_config proxy_ssl_config; bool tls_upgraded; @@ -1030,16 +1012,6 @@ struct connectdata { char *unix_domain_socket; bool abstract_unix_socket; #endif - -#ifdef USE_SSL - /* - * To avoid multiple malloc() calls, the ssl_connect_data structures - * associated with a connectdata struct are allocated in the same block - * as the latter. This field forces alignment to an 8-byte boundary so - * that this all works. - */ - long long *align_data__do_not_use; -#endif }; /* The end of connectdata. */ @@ -1308,7 +1280,7 @@ struct UrlState { /* set after initial USER failure, to prevent an authentication loop */ bool ftp_trying_alternative; - + bool wildcardmatch; /* enable wildcard matching */ int httpversion; /* the lowest HTTP version*10 reported by any server involved in this request */ bool expect100header; /* TRUE if we added Expect: 100-continue */ @@ -1358,6 +1330,9 @@ struct UrlState { struct Curl_easy *stream_depends_on; bool stream_depends_e; /* set or don't set the Exclusive bit */ int stream_weight; +#ifdef CURLDEBUG + bool conncache_lock; +#endif }; @@ -1443,7 +1418,7 @@ enum dupstring { STRING_RTSP_SESSION_ID, /* Session ID to use */ STRING_RTSP_STREAM_URI, /* Stream URI for this request */ STRING_RTSP_TRANSPORT, /* Transport for this session */ -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ @@ -1453,7 +1428,7 @@ enum dupstring { STRING_PROXY_SERVICE_NAME, /* Proxy service name */ #endif #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_SPNEGO) + defined(USE_SPNEGO) || defined(HAVE_GSSAPI) STRING_SERVICE_NAME, /* Service name */ #endif STRING_MAIL_FROM, @@ -1624,7 +1599,7 @@ struct UserDefined { bool http_keep_sending_on_error; /* for HTTP status codes >= 300 */ bool http_follow_location; /* follow HTTP redirects */ bool http_transfer_encoding; /* request compressed HTTP transfer-encoding */ - bool http_disable_hostname_check_before_authentication; + bool allow_auth_to_other_hosts; bool include_header; /* include received protocol headers in data output */ bool http_set_referer; /* is a custom referer used */ bool http_auto_referer; /* set "correct" referer when following location: */ @@ -1672,7 +1647,7 @@ struct UserDefined { /* Common RTSP header options */ Curl_RtspReq rtspreq; /* RTSP request type */ long rtspversion; /* like httpversion, for RTSP */ - bool wildcardmatch; /* enable wildcard matching */ + bool wildcard_enabled; /* enable wildcard matching */ curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer starts */ curl_chunk_end_callback chunk_end; /* called after part transferring diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index 185098e..131d9da 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -19,6 +19,7 @@ * KIND, either express or implied. * * RFC2831 DIGEST-MD5 authentication + * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication * ***************************************************************************/ @@ -34,6 +35,7 @@ #include "curl_base64.h" #include "curl_hmac.h" #include "curl_md5.h" +#include "curl_sha256.h" #include "vtls/vtls.h" #include "warnless.h" #include "strtok.h" @@ -144,6 +146,15 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ snprintf((char *) &dest[i * 2], 3, "%02x", source[i]); } +/* Convert sha256 chunk to RFC7616 -suitable ascii string*/ +static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ + unsigned char *dest) /* 65 bytes */ +{ + int i; + for(i = 0; i < 32; i++) + snprintf((char *) &dest[i * 2], 3, "%02x", source[i]); +} + /* Perform quoted-string escaping as described in RFC2616 and its errata */ static char *auth_digest_string_quoted(const char *source) { @@ -602,9 +613,22 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, digest->algo = CURLDIGESTALGO_MD5SESS; else if(strcasecompare(content, "MD5")) digest->algo = CURLDIGESTALGO_MD5; + else if(strcasecompare(content, "SHA-256")) + digest->algo = CURLDIGESTALGO_SHA256; + else if(strcasecompare(content, "SHA-256-SESS")) + digest->algo = CURLDIGESTALGO_SHA256SESS; + else if(strcasecompare(content, "SHA-512-256")) + digest->algo = CURLDIGESTALGO_SHA512_256; + else if(strcasecompare(content, "SHA-512-256-SESS")) + digest->algo = CURLDIGESTALGO_SHA512_256SESS; else return CURLE_BAD_CONTENT_ENCODING; } + else if(strcasecompare(value, "userhash")) { + if(strcasecompare(content, "true")) { + digest->userhash = TRUE; + } + } else { /* Unknown specifier, ignore it! */ } @@ -635,7 +659,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, } /* - * Curl_auth_create_digest_http_message() + * _Curl_auth_create_digest_http_message() * * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. @@ -654,20 +678,24 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen) +static CURLcode _Curl_auth_create_digest_http_message( + struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen, + void (*convert_to_ascii)(unsigned char *, unsigned char *), + void (*hash)(unsigned char *, const unsigned char *)) { CURLcode result; - unsigned char md5buf[16]; /* 16 bytes/128 bits */ - unsigned char request_digest[33]; - unsigned char *md5this; - unsigned char ha1[33]; /* 32 digits and 1 zero byte */ - unsigned char ha2[33]; /* 32 digits and 1 zero byte */ + unsigned char hashbuf[32]; /* 32 bytes/256 bits */ + unsigned char request_digest[65]; + unsigned char *hashthis; + unsigned char ha1[65]; /* 64 digits and 1 zero byte */ + unsigned char ha2[65]; /* 64 digits and 1 zero byte */ + char userh[65]; char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; @@ -692,6 +720,17 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, digest->cnonce = cnonce; } + if(digest->userhash) { + hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm); + if(!hashthis) + return CURLE_OUT_OF_MEMORY; + + CURL_OUTPUT_DIGEST_CONV(data, hashthis); + hash(hashbuf, hashthis); + free(hashthis); + convert_to_ascii(hashbuf, (unsigned char *)userh); + } + /* If the algorithm is "MD5" or unspecified (which then defaults to MD5): @@ -703,26 +742,29 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, unq(nonce-value) ":" unq(cnonce-value) */ - md5this = (unsigned char *) - aprintf("%s:%s:%s", userp, digest->realm, passwdp); - if(!md5this) + hashthis = (unsigned char *) + aprintf("%s:%s:%s", digest->userhash ? userh : userp, + digest->realm, passwdp); + if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - free(md5this); - auth_digest_md5_to_ascii(md5buf, ha1); + CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ + hash(hashbuf, hashthis); + free(hashthis); + convert_to_ascii(hashbuf, ha1); - if(digest->algo == CURLDIGESTALGO_MD5SESS) { + if(digest->algo == CURLDIGESTALGO_MD5SESS || + digest->algo == CURLDIGESTALGO_SHA256SESS || + digest->algo == CURLDIGESTALGO_SHA512_256SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); if(!tmp) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */ - Curl_md5it(md5buf, (unsigned char *) tmp); + hash(hashbuf, (unsigned char *) tmp); free(tmp); - auth_digest_md5_to_ascii(md5buf, ha1); + convert_to_ascii(hashbuf, ha1); } /* @@ -738,27 +780,32 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, 5.1.1 of RFC 2616) */ - md5this = (unsigned char *) aprintf("%s:%s", request, uripath); + hashthis = (unsigned char *) aprintf("%s:%s", request, uripath); if(digest->qop && strcasecompare(digest->qop, "auth-int")) { /* We don't support auth-int for PUT or POST at the moment. - TODO: replace md5 of empty string with entity-body for PUT/POST */ - unsigned char *md5this2 = (unsigned char *) - aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); - free(md5this); - md5this = md5this2; + TODO: replace hash of empty string with entity-body for PUT/POST */ + char hashed[65]; + unsigned char *hashthis2; + + hash(hashbuf, (const unsigned char *)""); + convert_to_ascii(hashbuf, (unsigned char *)hashed); + + hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed); + free(hashthis); + hashthis = hashthis2; } - if(!md5this) + if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - free(md5this); - auth_digest_md5_to_ascii(md5buf, ha2); + CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ + hash(hashbuf, hashthis); + free(hashthis); + convert_to_ascii(hashbuf, ha2); if(digest->qop) { - md5this = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s", + hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, @@ -767,19 +814,19 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, ha2); } else { - md5this = (unsigned char *) aprintf("%s:%s:%s", + hashthis = (unsigned char *) aprintf("%s:%s:%s", ha1, digest->nonce, ha2); } - if(!md5this) + if(!hashthis) return CURLE_OUT_OF_MEMORY; - CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ - Curl_md5it(md5buf, md5this); - free(md5this); - auth_digest_md5_to_ascii(md5buf, request_digest); + CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */ + hash(hashbuf, hashthis); + free(hashthis); + convert_to_ascii(hashbuf, request_digest); /* For test case 64 (snooped from a Mozilla 1.3a request) @@ -794,7 +841,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, characters. algorithm and qop with standard values only contain web-safe characters. */ - userp_quoted = auth_digest_string_quoted(userp); + userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); if(!userp_quoted) return CURLE_OUT_OF_MEMORY; @@ -858,6 +905,16 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, response = tmp; } + if(digest->userhash) { + /* Append the userhash */ + tmp = aprintf("%s, userhash=true", response); + free(response); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + response = tmp; + } + /* Return the output */ *outptr = response; *outlen = strlen(response); @@ -866,6 +923,58 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, } /* + * Curl_auth_create_digest_http_message() + * + * This is used to generate a HTTP DIGEST response message ready for sending + * to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The user name. + * passdwp [in] - The user's password. + * request [in] - The HTTP request. + * uripath [in] - The path of the HTTP uri. + * digest [in/out] - The digest data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, + const char *userp, + const char *passwdp, + const unsigned char *request, + const unsigned char *uripath, + struct digestdata *digest, + char **outptr, size_t *outlen) +{ + switch(digest->algo) { + case CURLDIGESTALGO_MD5: + case CURLDIGESTALGO_MD5SESS: + return _Curl_auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_md5_to_ascii, + Curl_md5it); + + case CURLDIGESTALGO_SHA256: + case CURLDIGESTALGO_SHA256SESS: + case CURLDIGESTALGO_SHA512_256: + case CURLDIGESTALGO_SHA512_256SESS: + return _Curl_auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha256it); + + default: + return CURLE_UNSUPPORTED_PROTOCOL; + } +} + +/* * Curl_auth_digest_cleanup() * * This is used to clean up the digest specific data. @@ -887,6 +996,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) digest->nc = 0; digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */ digest->stale = FALSE; /* default means normal, not stale */ + digest->userhash = FALSE; } #endif /* !USE_WINDOWS_SSPI */ diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h index 5722dce..8686c44 100644 --- a/Utilities/cmcurl/lib/vauth/digest.h +++ b/Utilities/cmcurl/lib/vauth/digest.h @@ -31,7 +31,11 @@ enum { CURLDIGESTALGO_MD5, - CURLDIGESTALGO_MD5SESS + CURLDIGESTALGO_MD5SESS, + CURLDIGESTALGO_SHA256, + CURLDIGESTALGO_SHA256SESS, + CURLDIGESTALGO_SHA512_256, + CURLDIGESTALGO_SHA512_256SESS }; /* This is used to extract the realm from a challenge message */ diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 50d9222..1e0d479 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -543,8 +543,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, else user = userp; - if(user) - userlen = strlen(user); + userlen = strlen(user); /* Get the machine's un-qualified host name as NTLM doesn't like the fully qualified domain name */ diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index ebd6006..1752e14 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,7 @@ #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" +#include "ssh.h" #include "curl_printf.h" #ifdef USE_ARES @@ -64,6 +65,18 @@ #define CURL_LIBSSH2_VERSION LIBSSH2_VERSION #endif +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#ifdef __SYMBIAN32__ +/* zlib pollutes the namespace with this definition */ +#undef WIN32 +#endif +#endif + +#ifdef HAVE_BROTLI +#include <brotli/decode.h> +#endif + void Curl_version_init(void); /* For thread safety purposes this function is called by global_init so that @@ -74,6 +87,18 @@ void Curl_version_init(void) curl_version_info(CURLVERSION_NOW); } +#ifdef HAVE_BROTLI +static size_t brotli_version(char *buf, size_t bufsz) +{ + uint32_t brotli_version = BrotliDecoderVersion(); + unsigned int major = brotli_version >> 24; + unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; + unsigned int patch = brotli_version & 0x00000FFF; + + return snprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); +} +#endif + char *curl_version(void) { static bool initialized; @@ -105,6 +130,14 @@ char *curl_version(void) left -= len; ptr += len; #endif +#ifdef HAVE_BROTLI + len = snprintf(ptr, left, "%s", " brotli/"); + left -= len; + ptr += len; + len = brotli_version(ptr, left); + left -= len; + ptr += len; +#endif #ifdef USE_ARES /* this function is only present in c-ares, not in the original ares */ len = snprintf(ptr, left, " c-ares/%s", ares_version(NULL)); @@ -144,6 +177,11 @@ char *curl_version(void) left -= len; ptr += len; #endif +#ifdef USE_LIBSSH + len = snprintf(ptr, left, " libssh/%s", CURL_LIBSSH_VERSION); + left -= len; + ptr += len; +#endif #ifdef USE_NGHTTP2 len = Curl_http2_ver(ptr, left); left -= len; @@ -232,10 +270,8 @@ static const char * const protocols[] = { #ifndef CURL_DISABLE_RTSP "rtsp", #endif -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH) || defined(USE_LIBSSH2) "scp", -#endif -#ifdef USE_LIBSSH2 "sftp", #endif #if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ @@ -327,6 +363,9 @@ static curl_version_info_data version_info = { #if defined(CURL_WITH_MULTI_SSL) | CURL_VERSION_MULTI_SSL #endif +#if defined(HAVE_BROTLI) + | CURL_VERSION_BROTLI +#endif , NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ @@ -337,17 +376,22 @@ static curl_version_info_data version_info = { NULL, /* libidn version */ 0, /* iconv version */ NULL, /* ssh lib version */ + 0, /* brotli_ver_num */ + NULL, /* brotli version */ }; curl_version_info_data *curl_version_info(CURLversion stamp) { static bool initialized; -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH) || defined(USE_LIBSSH2) static char ssh_buffer[80]; #endif #ifdef USE_SSL static char ssl_buffer[80]; #endif +#ifdef HAVE_BROTLI + static char brotli_buffer[80]; +#endif if(initialized) return &version_info; @@ -391,9 +435,18 @@ curl_version_info_data *curl_version_info(CURLversion stamp) #endif /* _LIBICONV_VERSION */ #endif -#ifdef USE_LIBSSH2 +#if defined(USE_LIBSSH2) snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION); version_info.libssh_version = ssh_buffer; +#elif defined(USE_LIBSSH) + snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh/%s", CURL_LIBSSH_VERSION); + version_info.libssh_version = ssh_buffer; +#endif + +#ifdef HAVE_BROTLI + version_info.brotli_ver_num = BrotliDecoderVersion(); + brotli_version(brotli_buffer, sizeof brotli_buffer); + version_info.brotli_version = brotli_buffer; #endif (void)stamp; /* avoid compiler warnings, we don't use this */ diff --git a/Utilities/cmcurl/lib/vtls/axtls.c b/Utilities/cmcurl/lib/vtls/axtls.c index 6b42708..9294f49 100644 --- a/Utilities/cmcurl/lib/vtls/axtls.c +++ b/Utilities/cmcurl/lib/vtls/axtls.c @@ -728,7 +728,7 @@ const struct Curl_ssl Curl_ssl_axtls = { Curl_axtls_connect, /* connect */ Curl_axtls_connect_nonblocking, /* connect_nonblocking */ Curl_axtls_get_internals, /* get_internals */ - Curl_axtls_close, /* close */ + Curl_axtls_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_axtls_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/cyassl.c b/Utilities/cmcurl/lib/vtls/cyassl.c index ba5ee15..46b71bf 100644 --- a/Utilities/cmcurl/lib/vtls/cyassl.c +++ b/Utilities/cmcurl/lib/vtls/cyassl.c @@ -76,9 +76,7 @@ and that's a problem since options.h hasn't been included yet. */ #endif #endif -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #include "urldata.h" #include "sendf.h" @@ -1003,7 +1001,7 @@ const struct Curl_ssl Curl_ssl_cyassl = { Curl_cyassl_connect, /* connect */ Curl_cyassl_connect_nonblocking, /* connect_nonblocking */ Curl_cyassl_get_internals, /* get_internals */ - Curl_cyassl_close, /* close */ + Curl_cyassl_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_cyassl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/darwinssl.c b/Utilities/cmcurl/lib/vtls/darwinssl.c index a98f433..53a7ec3 100644 --- a/Utilities/cmcurl/lib/vtls/darwinssl.c +++ b/Utilities/cmcurl/lib/vtls/darwinssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>. * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which @@ -22,7 +22,7 @@ ***************************************************************************/ /* - * Source file for all iOS and Mac OS X SecureTransport-specific code for the + * Source file for all iOS and macOS SecureTransport-specific code for the * TLS/SSL layer. No code but vtls.c should ever call or use these functions. */ @@ -39,9 +39,7 @@ #pragma clang diagnostic ignored "-Wtautological-pointer-compare" #endif /* __clang__ */ -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #include <Security/Security.h> /* For some reason, when building for iOS, the omnibus header above does @@ -50,12 +48,14 @@ #include <CoreFoundation/CoreFoundation.h> #include <CommonCrypto/CommonDigest.h> -/* The Security framework has changed greatly between iOS and different OS X +/* The Security framework has changed greatly between iOS and different macOS versions, and we will try to support as many of them as we can (back to Leopard and iOS 5) by using macros and weak-linking. - IMPORTANT: If TLS 1.1 and 1.2 support are important for you on OS X, then - you must build this project against the 10.8 SDK or later. */ + In general, you want to build this using the most recent OS SDK, since some + features require curl to be built against the latest SDK. TLS 1.1 and 1.2 + support, for instance, require the macOS 10.8 SDK or later. TLS 1.3 + requires the macOS 10.13 or iOS 11 SDK or later. */ #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 @@ -64,6 +64,7 @@ #define CURL_BUILD_IOS 0 #define CURL_BUILD_IOS_7 0 +#define CURL_BUILD_IOS_11 0 #define CURL_BUILD_MAC 1 /* This is the maximum API level we are allowed to use when building: */ #define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 @@ -71,10 +72,11 @@ #define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 #define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 #define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 +#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 /* These macros mean "the following code is present to allow runtime backward compatibility with at least this cat or earlier": - (You set this at build-time by setting the MACOSX_DEPLOYMENT_TARGET - environmental variable.) */ + (You set this at build-time using the compiler command line option + "-mmacos-version-min.") */ #define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 #define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 #define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 @@ -84,11 +86,14 @@ #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE #define CURL_BUILD_IOS 1 #define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 #define CURL_BUILD_MAC 0 #define CURL_BUILD_MAC_10_5 0 #define CURL_BUILD_MAC_10_6 0 #define CURL_BUILD_MAC_10_7 0 #define CURL_BUILD_MAC_10_8 0 +#define CURL_BUILD_MAC_10_9 0 +#define CURL_BUILD_MAC_10_13 0 #define CURL_SUPPORT_MAC_10_5 0 #define CURL_SUPPORT_MAC_10_6 0 #define CURL_SUPPORT_MAC_10_7 0 @@ -838,6 +843,30 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) return "TLS_RSA_PSK_WITH_NULL_SHA384"; break; #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + /* New ChaCha20+Poly1305 cipher-suites used by TLS 1.3: */ + case TLS_AES_128_GCM_SHA256: + return "TLS_AES_128_GCM_SHA256"; + break; + case TLS_AES_256_GCM_SHA384: + return "TLS_AES_256_GCM_SHA384"; + break; + case TLS_CHACHA20_POLY1305_SHA256: + return "TLS_CHACHA20_POLY1305_SHA256"; + break; + case TLS_AES_128_CCM_SHA256: + return "TLS_AES_128_CCM_SHA256"; + break; + case TLS_AES_128_CCM_8_SHA256: + return "TLS_AES_128_CCM_8_SHA256"; + break; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"; + break; + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"; + break; +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ } return "TLS_NULL_WITH_NULL_NULL"; } @@ -1171,6 +1200,15 @@ static CURLcode darwinssl_version_from_curl(SSLProtocol *darwinver, *darwinver = kTLSProtocol12; return CURLE_OK; case CURL_SSLVERSION_TLSv1_3: + /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */ +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + /* We can assume __builtin_available() will always work in the + 10.13/11.0 SDK: */ + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + *darwinver = kTLSProtocol13; + return CURLE_OK; + } +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ break; } return CURLE_SSL_CONNECT_ERROR; @@ -1184,12 +1222,27 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; long ssl_version = SSL_CONN_CONFIG(version); long ssl_version_max = SSL_CONN_CONFIG(version_max); + long max_supported_version_by_os; + + /* macOS 10.5-10.7 supported TLS 1.0 only. + macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. + macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3; + } + else { + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; + } +#else + max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ switch(ssl_version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ssl_version = CURL_SSLVERSION_TLSv1_0; - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + ssl_version_max = max_supported_version_by_os; break; } @@ -1198,7 +1251,7 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) ssl_version_max = ssl_version << 16; break; case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + ssl_version_max = max_supported_version_by_os; break; } @@ -1247,7 +1300,7 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex) true); break; case CURL_SSLVERSION_TLSv1_3: - failf(data, "DarwinSSL: TLS 1.3 is not yet supported"); + failf(data, "Your version of the OS does not support TLSv1.3"); return CURLE_SSL_CONNECT_ERROR; } } @@ -1326,7 +1379,16 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kTLSProtocol1); +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + if(__builtin_available(macOS 10.13, iOS 11.0, *)) { + (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol13); + } + else { + (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol12); + } +#else (void)SSLSetProtocolVersionMax(BACKEND->ssl_ctx, kTLSProtocol12); +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: @@ -2341,7 +2403,13 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) infof(data, "TLS 1.2 connection using %s\n", TLSCipherNameForNumber(cipher)); break; -#endif +#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ +#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 + case kTLSProtocol13: + infof(data, "TLS 1.3 connection using %s\n", + TLSCipherNameForNumber(cipher)); + break; +#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ default: infof(data, "Unknown protocol connection\n"); break; @@ -2932,7 +3000,7 @@ const struct Curl_ssl Curl_ssl_darwinssl = { Curl_darwinssl_connect, /* connect */ Curl_darwinssl_connect_nonblocking, /* connect_nonblocking */ Curl_darwinssl_get_internals, /* get_internals */ - Curl_darwinssl_close, /* close */ + Curl_darwinssl_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_darwinssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c index ba5faef..8f0cc0b 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ b/Utilities/cmcurl/lib/vtls/gskit.c @@ -61,9 +61,7 @@ #endif -#ifdef HAVE_LIMITS_H -# include <limits.h> -#endif +#include <limits.h> #include <curl/curl.h> #include "urldata.h" @@ -1375,7 +1373,7 @@ const struct Curl_ssl Curl_ssl_gskit = { Curl_gskit_connect, /* connect */ Curl_gskit_connect_nonblocking, /* connect_nonblocking */ Curl_gskit_get_internals, /* get_internals */ - Curl_gskit_close, /* close */ + Curl_gskit_close, /* close_one */ Curl_none_close_all, /* close_all */ /* No session handling for GSKit */ Curl_none_session_free, /* session_free */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index a844915..30b255b 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -1827,7 +1827,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { Curl_gtls_connect, /* connect */ Curl_gtls_connect_nonblocking, /* connect_nonblocking */ Curl_gtls_get_internals, /* get_internals */ - Curl_gtls_close, /* close */ + Curl_gtls_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_gtls_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index ce1f8eb..28251a3 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -1061,7 +1061,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { Curl_mbedtls_connect, /* connect */ Curl_mbedtls_connect_nonblocking, /* connect_nonblocking */ Curl_mbedtls_get_internals, /* get_internals */ - Curl_mbedtls_close, /* close */ + Curl_mbedtls_close, /* close_one */ Curl_mbedtls_close_all, /* close_all */ Curl_mbedtls_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index c0b7e63..a3ef37a 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -2365,7 +2365,7 @@ const struct Curl_ssl Curl_ssl_nss = { Curl_nss_connect, /* connect */ Curl_nss_connect_nonblocking, /* connect_nonblocking */ Curl_nss_get_internals, /* get_internals */ - Curl_nss_close, /* close */ + Curl_nss_close, /* close_one */ Curl_none_close_all, /* close_all */ /* NSS has its own session ID cache */ Curl_none_session_free, /* session_free */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 4253160..93faa6f 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -34,9 +34,7 @@ #ifdef USE_OPENSSL -#ifdef HAVE_LIMITS_H #include <limits.h> -#endif #include "urldata.h" #include "sendf.h" @@ -68,10 +66,7 @@ #include <openssl/rsa.h> #include <openssl/bio.h> #include <openssl/buffer.h> - -#ifdef HAVE_OPENSSL_PKCS12_H #include <openssl/pkcs12.h> -#endif #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include <openssl/ocsp.h> @@ -151,14 +146,13 @@ static unsigned long OpenSSL_version_num(void) /* * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 - * BoringSSL: supported since d28f59c27bac (committed 2015-11-19), the - * BORINGSSL_201512 macro from 2016-01-21 should be close enough. + * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) * LibreSSL: unsupported in at least 2.5.1 (explicitly check for it since it * lies and pretends to be OpenSSL 2.0.0). */ #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ !defined(LIBRESSL_VERSION_NUMBER)) || \ - defined(BORINGSSL_201512) + defined(OPENSSL_IS_BORINGSSL) #define HAVE_KEYLOG_CALLBACK #endif @@ -181,6 +175,8 @@ static unsigned long OpenSSL_version_num(void) "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" #endif +#define ENABLE_SSLKEYLOGFILE + #ifdef ENABLE_SSLKEYLOGFILE typedef struct ssl_tap_state { int master_key_length; @@ -263,11 +259,11 @@ static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) if(!session || !keylog_file_fp) return; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that * we have a valid SSL context if we have a non-NULL session. */ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); - master_key_length = + master_key_length = (int) SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH); #else if(ssl->s3 && session->master_key_length > 0) { @@ -445,14 +441,14 @@ static CURLcode Curl_ossl_seed(struct Curl_easy *data) size_t len = sizeof(randb); size_t i, i_max; for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { - struct curltime tv = curlx_tvnow(); + struct curltime tv = Curl_now(); Curl_wait_ms(1); tv.tv_sec *= i + 1; tv.tv_usec *= (unsigned int)i + 2; - tv.tv_sec ^= ((curlx_tvnow().tv_sec + curlx_tvnow().tv_usec) * + tv.tv_sec ^= ((Curl_now().tv_sec + Curl_now().tv_usec) * (i + 3)) << 8; - tv.tv_usec ^= (unsigned int) ((curlx_tvnow().tv_sec + - curlx_tvnow().tv_usec) * + tv.tv_usec ^= (unsigned int) ((Curl_now().tv_sec + + Curl_now().tv_usec) * (i + 4)) << 16; memcpy(&randb[i * sizeof(struct curltime)], &tv, sizeof(struct curltime)); @@ -653,7 +649,6 @@ int cert_stuff(struct connectdata *conn, case SSL_FILETYPE_PKCS12: { -#ifdef HAVE_OPENSSL_PKCS12_H FILE *f; PKCS12 *p12; EVP_PKEY *pri; @@ -740,10 +735,6 @@ int cert_stuff(struct connectdata *conn, if(!cert_done) return 0; /* failure! */ break; -#else - failf(data, "file type P12 for certificate not supported"); - return 0; -#endif } default: failf(data, "not supported file type '%s' for certificate", cert_type); @@ -837,12 +828,18 @@ int cert_stuff(struct connectdata *conn, EVP_PKEY_free(pktmp); } -#ifndef OPENSSL_NO_RSA +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) { /* If RSA is used, don't check the private key if its flags indicate * it doesn't support it. */ EVP_PKEY *priv_key = SSL_get_privatekey(ssl); - if(EVP_PKEY_id(priv_key) == EVP_PKEY_RSA) { + int pktype; +#ifdef HAVE_OPAQUE_EVP_PKEY + pktype = EVP_PKEY_id(priv_key); +#else + pktype = priv_key->type; +#endif + if(pktype == EVP_PKEY_RSA) { RSA *rsa = EVP_PKEY_get1_RSA(priv_key); if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK) check_privkey = FALSE; @@ -907,7 +904,7 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) static int Curl_ossl_init(void) { #ifdef ENABLE_SSLKEYLOGFILE - const char *keylog_file_name; + char *keylog_file_name; #endif OPENSSL_load_builtin_modules(); @@ -947,14 +944,22 @@ static int Curl_ossl_init(void) #endif #ifdef ENABLE_SSLKEYLOGFILE - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); - if(keylog_file_name && !keylog_file_fp) { - keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); - if(keylog_file_fp) { - if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) { - fclose(keylog_file_fp); - keylog_file_fp = NULL; + if(!keylog_file_fp) { + keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + if(keylog_file_name) { + keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); + if(keylog_file_fp) { +#ifdef WIN32 + if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) +#else + if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) +#endif + { + fclose(keylog_file_fp); + keylog_file_fp = NULL; + } } + Curl_safefree(keylog_file_name); } } #endif @@ -2408,8 +2413,8 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ #if defined(ENABLE_SSLKEYLOGFILE) && defined(HAVE_KEYLOG_CALLBACK) - if(keylog_file) { - SSL_CTX_set_keylog_callback(connssl->ctx, ossl_keylog_callback); + if(keylog_file_fp) { + SSL_CTX_set_keylog_callback(BACKEND->ctx, ossl_keylog_callback); } #endif @@ -3060,12 +3065,12 @@ static CURLcode servercert(struct connectdata *conn, ASN1_TIME_print(mem, X509_get0_notBefore(BACKEND->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " start date: %.*s\n", len, ptr); - rc = BIO_reset(mem); + (void)BIO_reset(mem); ASN1_TIME_print(mem, X509_get0_notAfter(BACKEND->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " expire date: %.*s\n", len, ptr); - rc = BIO_reset(mem); + (void)BIO_reset(mem); BIO_free(mem); @@ -3386,12 +3391,13 @@ static bool Curl_ossl_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; - if(BACKEND->handle) - /* SSL is in use */ - return (0 != SSL_pending(BACKEND->handle) || - (proxyssl->backend->handle && - 0 != SSL_pending(proxyssl->backend->handle))) ? - TRUE : FALSE; + + if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) + return TRUE; + + if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) + return TRUE; + return FALSE; } @@ -3636,7 +3642,7 @@ const struct Curl_ssl Curl_ssl_openssl = { Curl_ossl_connect, /* connect */ Curl_ossl_connect_nonblocking, /* connect_nonblocking */ Curl_ossl_get_internals, /* get_internals */ - Curl_ossl_close, /* close */ + Curl_ossl_close, /* close_one */ Curl_ossl_close_all, /* close_all */ Curl_ossl_session_free, /* session_free */ Curl_ossl_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/polarssl.c b/Utilities/cmcurl/lib/vtls/polarssl.c index fc0644f..df29fa9 100644 --- a/Utilities/cmcurl/lib/vtls/polarssl.c +++ b/Utilities/cmcurl/lib/vtls/polarssl.c @@ -923,7 +923,7 @@ const struct Curl_ssl Curl_ssl_polarssl = { Curl_polarssl_connect, /* connect */ Curl_polarssl_connect_nonblocking, /* connect_nonblocking */ Curl_polarssl_get_internals, /* get_internals */ - Curl_polarssl_close, /* close */ + Curl_polarssl_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_polarssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 9ca1431..85c64cf 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -1838,7 +1838,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_schannel_connect, /* connect */ Curl_schannel_connect_nonblocking, /* connect_nonblocking */ Curl_schannel_get_internals, /* get_internals */ - Curl_schannel_close, /* close */ + Curl_schannel_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_schannel_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index bb8fda4..def1d30 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -549,7 +549,7 @@ int Curl_ssl_getsock(struct connectdata *conn, void Curl_ssl_close(struct connectdata *conn, int sockindex) { DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); - Curl_ssl->close(conn, sockindex); + Curl_ssl->close_one(conn, sockindex); } CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex) @@ -1059,6 +1059,7 @@ bool Curl_none_false_start(void) return FALSE; } +#ifndef CURL_DISABLE_CRYPTO_AUTH CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, unsigned char *md5sum, size_t md5len UNUSED_PARAM) { @@ -1073,6 +1074,19 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen, Curl_MD5_final(MD5pw, md5sum); return CURLE_OK; } +#else +CURLcode Curl_none_md5sum(unsigned char *input UNUSED_PARAM, + size_t inputlen UNUSED_PARAM, + unsigned char *md5sum UNUSED_PARAM, + size_t md5len UNUSED_PARAM) +{ + (void)input; + (void)inputlen; + (void)md5sum; + (void)md5len; + return CURLE_NOT_BUILT_IN; +} +#endif static int Curl_multissl_init(void) { @@ -1108,7 +1122,7 @@ static void Curl_multissl_close(struct connectdata *conn, int sockindex) { if(multissl_init(NULL)) return; - Curl_ssl->close(conn, sockindex); + Curl_ssl->close_one(conn, sockindex); } static const struct Curl_ssl Curl_ssl_multi = { @@ -1133,7 +1147,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_multissl_connect, /* connect */ Curl_multissl_connect_nonblocking, /* connect_nonblocking */ Curl_multissl_get_internals, /* get_internals */ - Curl_multissl_close, /* close */ + Curl_multissl_close, /* close_one */ Curl_none_close_all, /* close_all */ Curl_none_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ @@ -1246,6 +1260,7 @@ static size_t Curl_multissl_version(char *buffer, size_t size) static int multissl_init(const struct Curl_ssl *backend) { const char *env; + char *env_tmp; int i; if(Curl_ssl != &Curl_ssl_multi) @@ -1259,7 +1274,7 @@ static int multissl_init(const struct Curl_ssl *backend) if(!available_backends[0]) return 1; - env = getenv("CURL_SSL_BACKEND"); + env = env_tmp = curl_getenv("CURL_SSL_BACKEND"); #ifdef CURL_DEFAULT_SSL_BACKEND if(!env) env = CURL_DEFAULT_SSL_BACKEND; @@ -1268,6 +1283,7 @@ static int multissl_init(const struct Curl_ssl *backend) for(i = 0; available_backends[i]; i++) { if(strcasecompare(env, available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; + curl_free(env_tmp); return 0; } } @@ -1275,6 +1291,7 @@ static int multissl_init(const struct Curl_ssl *backend) /* Fall back to first available backend */ Curl_ssl = available_backends[0]; + curl_free(env_tmp); return 0; } diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index f1a11ea..c5f9d4a 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -60,7 +60,7 @@ struct Curl_ssl { CURLcode (*connect_nonblocking)(struct connectdata *conn, int sockindex, bool *done); void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); - void (*close)(struct connectdata *conn, int sockindex); + void (*close_one)(struct connectdata *conn, int sockindex); void (*close_all)(struct Curl_easy *data); void (*session_free)(void *ptr); |