diff --git a/.gitignore b/.gitignore index 44b5eb56b..10498984c 100755 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,4 @@ __pycache__ *~ attic/world/*.c25519 attic/world/mkworld +/version.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fff7808e1..5f72081e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,127 @@ -# CMake build script for libzerotiercore.a +# CMake build script for ZeroTier One -cmake_minimum_required (VERSION 2.8) -project (zerotiercore) +cmake_minimum_required (VERSION 3.8) -set (PROJ_DIR ${PROJECT_SOURCE_DIR}) -set (ZT_DEFS -std=c++11) +# ZeroTier One Version Config -file(GLOB core_src_glob ${PROJ_DIR}/node/*.cpp) -add_library(zerotiercore STATIC ${core_src_glob}) +set(ZEROTIER_ONE_VERSION_MAJOR 1) +set(ZEROTIER_ONE_VERSION_MINOR 4) +set(ZEROTIER_ONE_VERSION_REVISION 2) +set(ZEROTIER_ONE_VERSION_BUILD 0) -target_compile_options(zerotiercore PRIVATE ${ZT_DEFS}) +# Set a default build type if none was specified +set(default_build_type "Release") +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + set(default_build_type "Debug") +endif() + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF) + +if (BUILD_CENTRAL_CONTROLLER) + find_package(PostgreSQL REQUIRED) + + set(ENABLE_SSL_SUPPORT OFF) + set(BUILD_SHARED_LIBS OFF) + set(BUILD_EXAMPLES OFF) + set(BUILD_TOOLS OFF) + set(BUILD_TESTS OFF) + set(BUILD_API_DOCS OFF) + add_subdirectory("ext/librabbitmq") +endif(BUILD_CENTRAL_CONTROLLER) + +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DZT_TRACE) +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +if(WIN32) + add_definitions(-DNOMINMAX) +else(WIN32) + if(APPLE) + + elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") + message("Setting Linux Compiler Flags ${CMAKE_BUILD_TYPE}") + add_compile_options( + -Wall + -Wno-deprecated + $<$:-g> + $<$:-O0> + $<$:-O3> + $<$:-fstackprotector> + $<$:-fPIE> + $<$:-O3> + $<$:-fstackprotector> + $<$:-fPIE> + $<$:-g> + ) + endif(APPLE) +endif(WIN32) + +project(zerotier-one) + +add_subdirectory(node) +add_subdirectory(controller) +add_subdirectory(osdep) +add_subdirectory(service) + +if(WIN32) + add_subdirectory("windows/WinUI") + add_subdirectory("windows/copyutil") + add_definitions(-DNOMINMAX) +endif(WIN32) + +set(libs + zt_service + zt_osdep + zt_core + zt_controller +) + +configure_file( + version.h.in + ${CMAKE_SOURCE_DIR}/version.h +) + +set(src + one.cpp + "ext/http-parser/http_parser.c" +) +set(headers + "ext/http-parser/http_parser.h" +) + +if(WIN32) + set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi) + set(src + ${src} + "windows/ZeroTierOne/ServiceBase.cpp" + "windows/ZeroTierOne/ServiceInstaller.cpp" + "windows/ZeroTierOne/ZeroTierOneService.cpp" + "windows/ZeroTierOne/ZeroTierOne.rc" + ) + set(headers + ${headers} + "windows/ZeroTierOne/ServiceBase.h" + "windows/ZeroTierOne/ServiceInstaller.h" + "windows/ZeroTierOne/ZeroTierOneService.h" + ) +else(WIN32) + set(libs ${libs} pthread) +endif(WIN32) + +if(BUILD_CENTRAL_CONTROLLER) + set(libs ${libs} rabbitmq-static ${PostgreSQL_LIBRARIES}) +endif(BUILD_CENTRAL_CONTROLLER) + +add_executable(${PROJECT_NAME} ${src} ${headers}) +target_link_libraries(${PROJECT_NAME} ${libs}) diff --git a/Jenkinsfile b/Jenkinsfile index 88989327e..457210adc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,11 @@ parallel 'centos7': { checkout scm stage('Build Centos 7') { - sh 'make -f make-linux.mk' + sh '''rm -rf build/ + mkdir build && cd build + CC=clang CXX=clang++ cmake .. + make -j4 + ''' } } catch (err) { diff --git a/controller/CMakeLists.txt b/controller/CMakeLists.txt new file mode 100644 index 000000000..c886b54b1 --- /dev/null +++ b/controller/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8) +project(zt_controller) + +if(WIN32) + add_definitions(-DNOMINMAX) +endif(WIN32) + +set(ctl_src + DB.cpp + DBMirrorSet.cpp + EmbeddedNetworkController.cpp + FileDB.cpp + LFDB.cpp + RabbitMQ.cpp +) + +set(ctl_hdr + DB.hpp + DBMirrorSet.hpp + EmbeddedNetworkController.hpp + FileDB.hpp + LFDB.hpp + RabbitMQ.hpp +) + +if(BUILD_CENTRAL_CONTROLLER) + add_definitions(-DZT_CONTROLLER_USE_LIBPQ) + include_directories("../ext/librabbitmq/librabbitmq" ${PostgreSQL_INCLUDE_DIRS}) + + set(ctl_src ${ctl_src} PostgreSQL.cpp) + set(ctl_hdr ${ctl_hdr} PostgreSQL.hpp) +endif(BUILD_CENTRAL_CONTROLLER) + +add_library(${PROJECT_NAME} STATIC ${ctl_src} ${ctl_hdr}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) + diff --git a/ext/librabbitmq/.clang-format b/ext/librabbitmq/.clang-format new file mode 100644 index 000000000..8aa6b1a85 --- /dev/null +++ b/ext/librabbitmq/.clang-format @@ -0,0 +1,47 @@ +--- +# BasedOnStyle: Google +AccessModifierOffset: -1 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: true +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerBinding: true +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 60 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2 +Cpp11BracedListStyle: true +Standard: Auto +IndentWidth: 2 +TabWidth: 8 +UseTab: Never +BreakBeforeBraces: Attach +IndentFunctionDeclarationAfterType: true +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 +... + diff --git a/ext/librabbitmq/.gitattributes b/ext/librabbitmq/.gitattributes new file mode 100644 index 000000000..a465161db --- /dev/null +++ b/ext/librabbitmq/.gitattributes @@ -0,0 +1,18 @@ +# Default for those who don't have core.autocrlf set +* text=auto + +# Things that should be treated like text (lines converted on checkout): +*.c text +*.h text +*.py text +*.cmake text +*.md text +# This is for the output of table_test +*.expected text +*.xml + +# Exceptions to the rule: +*.ac text eol=lf +*.am text eol=lf +*.m4 text eol=lf + diff --git a/ext/librabbitmq/.gitignore b/ext/librabbitmq/.gitignore new file mode 100644 index 000000000..1791e2800 --- /dev/null +++ b/ext/librabbitmq/.gitignore @@ -0,0 +1,73 @@ +*.la +*.lo +*.o +.deps +.dirstamp +.libs +/aclocal.m4 +/autom4te.cache +/bin* +/build +/compile +/config.guess +/config.h +/config.h.in +/config.h.in~ +/config.log +/config.status +/config.sub +/configure +/cscope.* +/depcomp +/install-sh +/libtool +/ltmain.sh +/missing +/stamp-h1 +/test-suite.log +INSTALL +Makefile +Makefile.in +examples/amqp_bind +examples/amqp_connect_timeout +examples/amqp_consumer +examples/amqp_exchange_declare +examples/amqp_listen +examples/amqp_listenq +examples/amqp_producer +examples/amqp_rpc_sendstring_client +examples/amqp_sendstring +examples/amqp_unbind +examples/amqps_bind +examples/amqps_connect_timeout +examples/amqps_consumer +examples/amqps_exchange_declare +examples/amqps_listen +examples/amqps_listenq +examples/amqps_producer +examples/amqps_sendstring +examples/amqps_unbind +librabbitmq.pc +test-driver +tests/*.log +tests/*.trs +tests/test_hostcheck +tests/test_parse_url +tests/test_status_enum +tests/test_tables +tools/amqp-consume +tools/amqp-declare-queue +tools/amqp-delete-queue +tools/amqp-get +tools/amqp-publish +tools/doc/*.1 +tools/doc/*.7 +tools/doc/man-date.ent +.ycm_extra_conf.py? +.DS_Store + +# Ignore editor swap files +*~ +*.sw? +.#* +\#*# diff --git a/ext/librabbitmq/.gitmodules b/ext/librabbitmq/.gitmodules new file mode 100644 index 000000000..b7f37f732 --- /dev/null +++ b/ext/librabbitmq/.gitmodules @@ -0,0 +1,6 @@ +[submodule "codegen"] + path = codegen + url = https://github.com/rabbitmq/rabbitmq-codegen.git +[submodule "travis/run-clang-format"] + path = travis/run-clang-format + url = https://github.com/Sarcasm/run-clang-format.git diff --git a/ext/librabbitmq/.travis.yml b/ext/librabbitmq/.travis.yml new file mode 100644 index 000000000..5d26e0f27 --- /dev/null +++ b/ext/librabbitmq/.travis.yml @@ -0,0 +1,92 @@ +# Travis-CI Build for rabbitmq-c +# see travis-ci.org for details + +language: c + +dist: trusty +# Currently libpopt-dev is not on the list of whitelisted apt-packages. +sudo: true + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "gDwqo3jHj+HHGzFKnxL/nwZhbVeh2pItw0TbeaHcLtWubUZaf85ViEQRaXPyfnbG7l0OEQq+PjyhKAfvViVq2NP0lGeeu4VM5uMZJhsCLN594BJr39Y4XzOapg0O8mEMhQ0DU2u1Zo4LMgEcRz67aosVQOj6QV30tOzp9fnxn9U=" + +services: + - rabbitmq + +matrix: + include: + # Note that the first compiler in the matrix must be gcc, so that the + # coverity_scan branch hack below works correctly. + - compiler: gcc + os: linux + env: CONFIG=cmake + - compiler: gcc + os: linux + env: CONFIG=format + - compiler: gcc + os: linux + env: CONFIG=coverage + - compiler: clang + os: linux + env: CONFIG=cmake + - compiler: clang + os: linux + env: CONFIG=asan + - compiler: clang + os: linux + env: CONFIG=tsan + - compiler: clang + os: linux + env: CONFIG=scan-build + - compiler: clang + os: osx + env: CONFIG=cmake + - compiler: gcc + os: linux + env: NAME="openssl-1.1.0" CONFIG=cmake + addons: + apt: + sources: + - sourceline: 'ppa:ondrej/nginx-mainline' + packages: + - libssl1.1 + - openssl + - libssl-dev + + allow_failures: + - compiler: clang + os: linux + env: CONFIG=tsan + +before_install: + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt-add-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" + sudo apt-get -q update; + sudo apt-get install -y clang-3.9 clang-format-3.9 libpopt-dev; + fi + # ugly hack; if running a coverity scan abort all except the 1st build + # see note re gcc compiler above needing to be 1st + # also note that branch_pattern & the TRAVIS_BRANCH check must match + # unfortunately COVERITY_SCAN_BRANCH isn't defined until later in the + # build process + - if ([[ "${TRAVIS_JOB_NUMBER##*.}" != "1" ]] && [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]); then false ; fi + + +script: + # Don't bother building if this is being done in the coverity_scan branch. + - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./travis.sh $CONFIG ; fi + +addons: + coverity_scan: + project: + name: "alanxz/rabbitmq-c" + description: "C AMQP client for RabbitMQ" + notification_email: alan.antonuk@gmail.com + build_command_prepend: mkdir build && pushd build && cmake .. && popd + build_command: cmake --build ./build + branch_pattern: coverity_scan diff --git a/ext/librabbitmq/.ycm_extra_conf.py b/ext/librabbitmq/.ycm_extra_conf.py new file mode 100644 index 000000000..d9ef11c65 --- /dev/null +++ b/ext/librabbitmq/.ycm_extra_conf.py @@ -0,0 +1,157 @@ +# This file is NOT licensed under the GPLv3, which is the license for the rest +# of YouCompleteMe. +# +# Here's the license text for this file: +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + +import os +import ycm_core + +# These are the compilation flags that will be used in case there's no +# compilation database set (by default, one is not set). +# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. +flags = [ +'-Wall', +'-Wextra', +# THIS IS IMPORTANT! Without a "-std=" flag, clang won't know which +# language to use when compiling headers. So it will guess. Badly. So C++ +# headers will be compiled as C headers. You don't want that so ALWAYS specify +# a "-std=". +# For a C project, you would set this to something like 'c99' instead of +# 'c++11'. +'-std=gnu90', +# ...and the same thing goes for the magic -x option which specifies the +# language that the files to be compiled are written in. This is mostly +# relevant for c++ headers. +# For a C project, you would set this to 'c' instead of 'c++'. +'-x', +'c', +'-I', './librabbitmq', +'-I', './librabbitmq/unix', +'-D', 'HAVE_POLL', +] + + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# +# You can get CMake to generate this file for you by adding: +# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) +# to your CMakeLists.txt file. +# +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = '' + +if os.path.exists( compilation_database_folder ): + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + +SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def IsHeaderFile( filename ): + extension = os.path.splitext( filename )[ 1 ] + return extension in [ '.h', '.hxx', '.hpp', '.hh' ] + + +def GetCompilationInfoForFile( filename ): + # The compilation_commands.json file generated by CMake does not have entries + # for header files. So we do our best by asking the db for flags for a + # corresponding source file, if any. If one exists, the flags for that file + # should be good enough. + if IsHeaderFile( filename ): + basename = os.path.splitext( filename )[ 0 ] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if os.path.exists( replacement_file ): + compilation_info = database.GetCompilationInfoForFile( + replacement_file ) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile( filename ) + + +def FlagsForFile( filename, **kwargs ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = GetCompilationInfoForFile( filename ) + if not compilation_info: + relative_to = DirectoryOfThisScript() + return { + 'flags': MakeRelativePathsInFlagsAbsolute( flags, relative_to ), + 'do_cache': True + } + + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/ext/librabbitmq/AUTHORS b/ext/librabbitmq/AUTHORS new file mode 100644 index 000000000..bd0070cfc --- /dev/null +++ b/ext/librabbitmq/AUTHORS @@ -0,0 +1,2 @@ +Tony Garnock-Jones +The RabbitMQ team diff --git a/ext/librabbitmq/CMakeLists.txt b/ext/librabbitmq/CMakeLists.txt new file mode 100644 index 000000000..b5a931141 --- /dev/null +++ b/ext/librabbitmq/CMakeLists.txt @@ -0,0 +1,343 @@ +cmake_minimum_required(VERSION 2.8.12) +project(rabbitmq-c "C") + +# Enable MACOSX_RPATH by default. See: cmake --help-policy CMP0042 +if (POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +# Follow all steps below in order to calculate new ABI version when updating the library +# NOTE: THIS IS UNRELATED to the actual project version +# +# 1. If the library source code has changed at all since the last update, then increment revision +# 2. If any interfaces have been added, removed, or changed since the last update, increment current and set revision to 0. +# 3. If any interfaces have been added since the last public release, then increment age. +# 4. If any interfaces have been removed since the last public release, then set age to 0. + +set(RMQ_SOVERSION_CURRENT 7) +set(RMQ_SOVERSION_REVISION 0) +set(RMQ_SOVERSION_AGE 3) + +math(EXPR RMQ_SOVERSION_MAJOR "${RMQ_SOVERSION_CURRENT} - ${RMQ_SOVERSION_AGE}") +math(EXPR RMQ_SOVERSION_MINOR "${RMQ_SOVERSION_AGE}") +math(EXPR RMQ_SOVERSION_PATCH "${RMQ_SOVERSION_REVISION}") + +set(RMQ_VERSION ${RMQ_SOVERSION_MAJOR}.${RMQ_SOVERSION_MINOR}.${RMQ_SOVERSION_PATCH}) +set(RMQ_SOVERSION ${RMQ_SOVERSION_MAJOR}) + +file(STRINGS librabbitmq/amqp.h _API_VERSION_MAJOR REGEX "^#define AMQP_VERSION_MAJOR [0-9]+$") +file(STRINGS librabbitmq/amqp.h _API_VERSION_MINOR REGEX "^#define AMQP_VERSION_MINOR [0-9]+$") +file(STRINGS librabbitmq/amqp.h _API_VERSION_PATCH REGEX "^#define AMQP_VERSION_PATCH [0-9]+$") + +string(REGEX MATCH "[0-9]+" _API_VERSION_MAJOR ${_API_VERSION_MAJOR}) +string(REGEX MATCH "[0-9]+" _API_VERSION_MINOR ${_API_VERSION_MINOR}) +string(REGEX MATCH "[0-9]+" _API_VERSION_PATCH ${_API_VERSION_PATCH}) + +# VERSION to match what is in autotools +set(VERSION ${_API_VERSION_MAJOR}.${_API_VERSION_MINOR}.${_API_VERSION_PATCH}) + +if (CMAKE_GENERATOR MATCHES ".*(Make|Ninja).*" + AND NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + message(STATUS "CMAKE_BUILD_TYPE not specified. Creating ${CMAKE_BUILD_TYPE} build") +endif() + +include(TestCInline) +include(CheckSymbolExists) +include(CheckLibraryExists) +include(CMakePushCheckState) +include(GNUInstallDirs) +include(CheckCCompilerFlag) + +# Detect if we need to link against a socket library: +cmake_push_check_state() +if (WIN32) + # Always use WinSock2 on Windows + set(SOCKET_LIBRARIES ws2_32) +else () + # Is it in the default link? + check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO) + if (NOT (HAVE_GETADDRINFO EQUAL 1)) + SET(CMAKE_REQUIRED_LIBRARIES "socket") + check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO2) + if (HAVE_GETADDRINFO2 EQUAL 1) + set(SOCKET_LIBRARIES socket) + else () + SET(CMAKE_REQUIRED_LIBRARIES "socket;nsl") + check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO3) + if (HAVE_GETADDRINFO3 EQUAL 1) + set(SOCKET_LIBRARIES socket nsl) + else () + message(FATAL_ERROR "Cannot find name resolution library (containing symbol getaddrinfo)") + endif () + endif () + endif () + + set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES}) + check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET) + if (NOT HAVE_SOCKET EQUAL 1) + set(CMAKE_REQUIRED_LIBRARIES socket ${SOCKET_LIBRARIES}) + check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET2) + if (HAVE_SOCKET2 EQUAL 1) + set(SOCKET_LIBRARIES socket ${SOCKET_LIBRARIES}) + else () + set(CMAKE_REQUIRED_LIBRARIES socket nsl ${SOCKET_LIBRARIES}) + check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET3) + if (HAVE_SOCKET3 EQUAL 1) + set(SOCKET_LIBRARIES socket nsl ${SOCKET_LIBRARIES}) + else () + message(FATAL_ERROR "Cannot find socket library (containing symbol socket)") + endif () + endif () + endif () +endif () +cmake_pop_check_state() + +cmake_push_check_state() +set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES}) +check_symbol_exists(poll poll.h HAVE_POLL) +if (NOT HAVE_POLL) + if (WIN32) + set(HAVE_SELECT 1) + else() + check_symbol_exists(select sys/select.h HAVE_SELECT) + endif() + if (NOT HAVE_SELECT) + message(FATAL_ERROR "rabbitmq-c requires poll() or select() to be available") + endif() +endif() +cmake_pop_check_state() + +check_library_exists(rt clock_gettime "time.h" CLOCK_GETTIME_NEEDS_LIBRT) +check_library_exists(rt posix_spawnp "spawn.h" POSIX_SPAWNP_NEEDS_LIBRT) +if (CLOCK_GETTIME_NEEDS_LIBRT OR POSIX_SPAWNP_NEEDS_LIBRT) + set(LIBRT rt) +endif() + +option(ENABLE_SSL_SUPPORT "Enable SSL support" ON) + +if (ENABLE_SSL_SUPPORT) + find_package(OpenSSL 0.9.8 REQUIRED) + + cmake_push_check_state() + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + cmake_pop_check_state() +endif() + +if (MSVC) + set(CMAKE_C_FLAGS "/W4 /nologo ${CMAKE_C_FLAGS}") +elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang") + set(CMAKE_C_FLAGS "-Wall -Wextra -Wstrict-prototypes -Wno-unused-function -fno-common -fvisibility=hidden ${CMAKE_C_FLAGS}") +elseif (CMAKE_COMPILER_IS_GNUCC) + set(RMQ_C_FLAGS "-Wall -Wextra -Wstrict-prototypes -Wno-unused-function -fno-common") + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_GREATER 4.0 OR GCC_VERSION VERSION_EQUAL 4.0) + set(RMQ_C_FLAGS "${RMQ_C_FLAGS} -fvisibility=hidden") + endif() + set(CMAKE_C_FLAGS "${RMQ_C_FLAGS} ${CMAKE_C_FLAGS}") +endif () + +CHECK_C_COMPILER_FLAG("-std=gnu90" HAVE_GNU90) +if (HAVE_GNU90) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90") +else() + CHECK_C_COMPILER_FLAG("-std=c90" HAVE_C90) + if (HAVE_C90) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c90") + endif() +endif() + +option(REGENERATE_AMQP_FRAMING "Regenerate amqp_framing.h/amqp_framing.c sources (for developer use)" OFF) +mark_as_advanced(REGENERATE_AMQP_FRAMING) + +if (REGENERATE_AMQP_FRAMING) + find_package(PythonInterp) + if (NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR "REGENERATE_AMQP_FRAMING requires Python to be available") + endif () + + #Determine Python Version: + if(PYTHON_EXECUTABLE) + execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c + "import sys; sys.stdout.write(';'.join([str(x) for x in sys.version_info[:3]]))" + OUTPUT_VARIABLE _VERSION + RESULT_VARIABLE _PYTHON_VERSION_RESULT + ERROR_QUIET) + if(NOT _PYTHON_VERSION_RESULT) + string(REPLACE ";" "." PYTHON_VERSION_STRING "${_VERSION}") + list(GET _VERSION 0 PYTHON_VERSION_MAJOR) + list(GET _VERSION 1 PYTHON_VERSION_MINOR) + list(GET _VERSION 2 PYTHON_VERSION_PATCH) + if(PYTHON_VERSION_PATCH EQUAL 0) + # it's called "Python 2.7", not "2.7.0" + string(REGEX REPLACE "\\.0$" "" PYTHON_VERSION_STRING "${PYTHON_VERSION_STRING}") + endif() + else() + # sys.version predates sys.version_info, so use that + execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.version)" + OUTPUT_VARIABLE _VERSION + RESULT_VARIABLE _PYTHON_VERSION_RESULT + ERROR_QUIET) + if(NOT _PYTHON_VERSION_RESULT) + string(REGEX REPLACE " .*" "" PYTHON_VERSION_STRING "${_VERSION}") + string(REGEX REPLACE "^([0-9]+)\\.[0-9]+.*" "\\1" PYTHON_VERSION_MAJOR "${PYTHON_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.([0-9])+.*" "\\1" PYTHON_VERSION_MINOR "${PYTHON_VERSION_STRING}") + if(PYTHON_VERSION_STRING MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+.*") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PYTHON_VERSION_PATCH "${PYTHON_VERSION_STRING}") + else() + set(PYTHON_VERSION_PATCH "0") + endif() + else() + # sys.version was first documented for Python 1.5, so assume + # this is older. + set(PYTHON_VERSION_STRING "1.4") + set(PYTHON_VERSION_MAJOR "1") + set(PYTHON_VERSION_MAJOR "4") + set(PYTHON_VERSION_MAJOR "0") + endif() + endif() + unset(_PYTHON_VERSION_RESULT) + unset(_VERSION) + endif(PYTHON_EXECUTABLE) + + # If we're running v3.x look for a 2to3 utility + if (PYTHON_VERSION_MAJOR GREATER 2) + get_filename_component(PYTHON_EXE_DIR ${PYTHON_EXECUTABLE} PATH) + find_program(PYTHON_2TO3_EXECUTABLE + NAMES 2to3 + HINTS ${PYTHON_EXE_DIR} + ) + + if ("PYTHON_2TO3_EXECUTABLE-NOTFOUND" STREQUAL PYTHON_2TO3_EXECUTABLE) + message(FATAL_ERROR "Unable to find 2to3 python utility, specify python 2.7 or specify 2to3 utility") + endif () + endif (PYTHON_VERSION_MAJOR GREATER 2) + + + #check for json or simplejson + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import json" + RESULT_VARIABLE CHECK_PYTHON_JSON_FAILED + ) + + if (CHECK_PYTHON_JSON_FAILED) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import simplejson" + RESULT_VARIABLE CHECK_PYTHON_SIMPLEJSON_FAILED + ) + + if (CHECK_PYTHON_SIMPLEJSON_FAILED) + message(FATAL_ERROR "REGENERATE_AMQP_FRAMING requires a python with json or simplejson modules") + endif (CHECK_PYTHON_SIMPLEJSON_FAILED) + endif (CHECK_PYTHON_JSON_FAILED) + + + find_path(AMQP_CODEGEN_DIR + amqp_codegen.py + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/codegen + ${CMAKE_CURRENT_SOURCE_DIR}/rabbitmq-codegen + ${CMAKE_CURRENT_SOURCE_DIR}/../rabbitmq-codegen + DOC "Path to directory containing amqp_codegen.py (rabbitmq-codegen)" + NO_DEFAULT_PATH + ) + + if (AMQP_CODEGEN_DIR STREQUAL "AMQP_CODEGEN_DIR-NOTFOUND") + message(SEND_ERROR "REGENERATE_AMQP_FRAMING requires the amqp_codegen.py script. If this is a git clone you can:\n\ngit submodule init\ngit submodule update\n\n Or set AMQP_CODEGEN_DIR to directory containing amqp_codegen.py") + else () + message(STATUS "Found amqp_codegen.py in ${AMQP_CODEGEN_DIR}") + endif() +endif (REGENERATE_AMQP_FRAMING) + +find_package(POPT) +find_package(XmlTo) +find_package(Doxygen) + +if (POPT_FOUND AND XmlTo_FOUND) + set(DO_DOCS ON) +endif() + + +option(BUILD_SHARED_LIBS "Build rabbitmq-c as a shared library" ON) +option(BUILD_STATIC_LIBS "Build rabbitmq-c as a static library" ON) + +option(BUILD_EXAMPLES "Build Examples" ON) +option(BUILD_TOOLS "Build Tools (requires POPT Library)" ${POPT_FOUND}) +option(BUILD_TOOLS_DOCS "Build man pages for Tools (requires xmlto)" ${DO_DOCS}) +option(BUILD_TESTS "Build tests (run tests with make test)" ON) +option(BUILD_API_DOCS "Build Doxygen API docs" ${DOXYGEN_FOUND}) + +if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) + message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON to build") +endif() + +add_subdirectory(librabbitmq) + +if (BUILD_EXAMPLES) + add_subdirectory(examples) +endif () + +if (BUILD_TOOLS) + if (POPT_FOUND) + add_subdirectory(tools) + else () + message(WARNING "POpt library was not found. Tools will not be built") + endif () +endif () + +if (BUILD_TESTS) + if (NOT BUILD_STATIC_LIBS) + message(FATAL_ERROR + "Tests can only be built against static libraries " + "(set BUILD_STATIC_LIBS=ON)") + endif () + enable_testing() + add_subdirectory(tests) +endif (BUILD_TESTS) + +if (BUILD_API_DOCS) + if (NOT DOXYGEN_FOUND) + message(FATAL_ERROR "Doxygen is required to build the API documentation") + endif () + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY) + + add_custom_target(docs + COMMAND ${DOXYGEN_EXECUTABLE} + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs + DEPENDS rabbitmq + COMMENT "Generating API documentation" + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in + ) +endif () + +set(libs_private ${SOCKET_LIBRARIES} ${LIBRT}) +if (ENABLE_SSL_SUPPORT) + set(requires_private "openssl") + set(libs_private ${libs_private} ${CMAKE_THREAD_LIBS_INIT}) +endif() + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\${prefix}") +set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") +set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + +configure_file(cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq/config.h) +configure_file(librabbitmq.pc.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc @ONLY) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) + +if (BUILD_SHARED_LIBS) + message(STATUS "Building rabbitmq as a shared library - yes") +else () + message(STATUS "Building rabbitmq as a shared library - no") +endif () + +if (BUILD_STATIC_LIBS) + message(STATUS "Building rabbitmq as a static library - yes") +else () + message(STATUS "Building rabbitmq as a static library - no") +endif () diff --git a/ext/librabbitmq/CONTRIBUTING.md b/ext/librabbitmq/CONTRIBUTING.md new file mode 100644 index 000000000..20c2e4d77 --- /dev/null +++ b/ext/librabbitmq/CONTRIBUTING.md @@ -0,0 +1,26 @@ +Contributing to rabbitmq-c +========================== + +Thanks for contributing to rabbitmq-c. I firmly believe that participation helps +make open source software great. With that there are a few things that can be +done to make our interaction a bit smoother. + +Please use the following guidelines when creating an issue or submitting a +pull request + +Creating an issue +----------------- +When submitting an issue its helpful to know the following + - What version of rabbitmq-c are you using? + - What operating system and version are you running on? + - What compiler and version are you running? + - + - If its a build system issue: which build system are you using ( + + +Submitting a pull-request +------------------------- +I love to get code contributions, a few things that can help out: + - Make sure your commits are rebased on the current master branch + - Please collapse your commits down to a couple logical commits + diff --git a/ext/librabbitmq/ChangeLog.md b/ext/librabbitmq/ChangeLog.md new file mode 100644 index 000000000..f9d7e4002 --- /dev/null +++ b/ext/librabbitmq/ChangeLog.md @@ -0,0 +1,221 @@ +# Change Log +## v0.9.0 - 2018-05-08 +### Added: +- amqp-publish: added support for specifying headers via the -H flag +- Add support for specifying timeout for amqp_login calls via + amqp_set_handshake_timeout +- Add support for specifying timeouts in RPC-style AMQP methods via + amqp_set_rpc_timeout +- Add define for `AMQP_DEFAULT_VHOST` +- Support for SSL SNI +- Support for OpenSSL v1.1.0 + +### Changed: +- rabbitmq-c now requires Windows Vista or better +- rabbitmq-c enables TCP keep-alive by default on platforms that support it +- dropped support for compiling rabbitmq-c without threading support +- OpenSSL is no longer un-intialized automatically by default. OpenSSL can be + explicitly initialized by calling amqp_initialize_ssl_library and + uninitialized by calling amqp_uninitialize_ssl_library. + +### Fixed: +- Correct bugs in processing of --url flag in tools (#364). +- Improve documentation on AMQP_SASL_METHOD_EXTERNAL (#349) +- Improve support for compiling under mingw-w64 +- Better support for handing SIGPIPE on Linux over SSL (#401) +- Improve publish performance on Linux by not specifying MSG_MORE on last part + of message. +- Fix connection logic where multiple hostnames won't be tried if connection to + doesn't fail immediately (#430) + +### Removed: +- autotools build system has been removed +- many duplicate amqps_* examples, they did not add a lot of value + + +## v0.8.0 - 2016-04-09 +### Added: +- SSL: peer certificate and hostname validation can now be controlled separately + using `amqp_ssl_socket_set_verify_peer` and + `amqp_ssl_socket_set_verify_hostname`. +- SSL: the desire SSL version range can now be specified using the + `amqp_ssl_socket_set_ssl_versions` function. +- Add flags to SSL examples on controlling hostname verification. + +### Changed: +- SSL: SSLv2, and SSLv3 have been disabled by default. +- SSL: OpenSSL hostname validation has been improved. +- Win32 debug information is built with /Z7 on MSVC to embed debug info instead + of using a .pdb + +### Fixed: +- Connection failure results in hang on Win32 (#297, #346) +- Rabbitmq-c may block when attempting to close an SSL socket (#313) +- amqp_parse_url does not correctly initialize default parameters (#319) +- x509 objects are leaked in verify_hostname (#323) +- TCP_NOPUSH doesn't work under cygwin (#335) + +### Deprecated +- SSL: `amqp_ssl_socket_set_verify` is being replaced by + `amqp_ssl_socket_set_verify_peer` and `amqp_ssl_socket_set_verify_hostname`. + +### Removed: +- OpenVMS build system and related files. +- Unmaintained PolarSSL, CyaSSL, and gnuTLS SSL backends + +## Changes since v0.7.0 (a.k.a., v0.7.1) +- `41fa9df` Autoconf: add missing files in build system +- `ef73c06` Win32: Use WSAEWOULDBLOCK instead of EWOULDBLOCK on Win32 +- `ceca348` CI: use travis-ci container based builds +- `393e2df` Lib: if channel_max is 0 use server's channel_max +- `ff47574` Lib: fix build on OpenBSD +- `8429496...0ac6430` CI: enable CI on Mac OS X in travis-ci + +## Changes since v0.6.0 (a.k.a., v0.7.0) +- `3379812` Tools: Add support for heartbeats +- `d7029db` CI: Add continuous integration on Win32 using Appveyor +- `a5f7ffb` Tests: only link against static libraries +- `a16ad45...9cf7a3b` Lib: add support for EXTERNAL SASL method +- `038a9ed` Lib: fix incorrect parameters to WSAPoll on Win32 +- `a240c69...14ae307` Lib: use non-blocking sockets internally +- `8d1d5cc`, `5498dc6` Lib: simplify timer/timeout logic +- `61fc4e1` Lib: add support for heartbeat checks in blocking send calls +- `f462c0f...3546a70` Lib: Fix warnings on Win32 +- `ba9d8ba...112a54d` Lib: Add support for RabbitMQ auth failure extension +- `fb8e318` Lib: allow calling functions to override client-properties +- `3ef3f5f` examples: replace usleep() with nanosleep() +- `9027a94` Lib: add AMQP_VERSION code +- `9ee1718` Lib: fix res maybe returned uninitialized in amqp_merge_capbilities +- `22a36db` Lib: Fix SSL_connection status check +- `abbefd4` Lib: Fix issues with c89 compatiblity +- `2bc1f9b...816cbfc` Lib: perf improvements when sending small messages by + hinting to the OS message boundaries. +- `be2e6dd...784a0e9` Lib: add select()-based timeout implementation +- `91db548...8d77b4c` CI: add ubsan, asan, and tsan CI builds + +## Changes since v0.5.2 (a.k.a., v0.6.0) +- `e1746f9` Tools: Enable support for SSL in tools. +- `9626dd5` Lib: ABI CHANGE: enable support for auto_delete, internal flags to + amqp_exchange_declare +- `ee54e27`, `656f833` Lib: check for double-close in SSL/TCP socket impl +- `cf2760d` Lib: allocate struct when method has no field. +- `513ad4a` Lib: add support for SANs in OpenSSL socket impl. +- `5348c69` Lib: add functions to get negotiated frame_max and heartbeat parms. + +## Changes since v0.5.1 (a.k.a., v0.5.2) +- `fcdf0f8` Autoconf: check for htonll as declaration in a header file +- `5790ec7` SSL: correctly report hostname verification errors. +- `d60c28c` Build: disable OpenSSL deprecation warnings on OSX +- `072191a` Lib: include platform, version and copyright in AMQP handshake +- `8b448c6` Examples: print message body in amqp[s]_listen[q] examples +- `7188e5d` Tools: Add flag to set prefetch for amqp-consume tool + +## Changes since v0.5.0 (a.k.a., v0.5.1) +### Enhancements: +- `a566929` SSL: Add support for wildcards in hostname verification (Mike + Steinert) +- `a78aa8a` Lib: Use poll(2) instead of select(2) for timeouts on sockets. +- `357bdb3` Lib: support for specifying frame and decoding pool sizes. (Mike + Stitt) +- `8956003` Lib: improve invalid frame detection code. + +### Bug fixes: +- `b852f84` Lib: Add missing amqp_get_server_properties() function. +- `7001e82` Lib: Add missing ssize_t on Win32 (emazv72) +- `c2ce2cb` Lib: Correctly specify WINVER on Win32 when unspecified. +- `fe844e4` CMake: specify -DHAVE_CONFIG_H in examples. +- `932de5f` Lib: correct time computation on Win32 (jestor) +- `3e83192` HPUX: use gethrtime on HP-UX for timers. +- `cb1b44e` HPUX: correct include location of sys/uio.h +- `8ce585d` Lib: incorrect OOM condition when 0-lenth exchange name is received. +- `c7716b8` CMake: correct htonll detection code on platforms defined with a + macro. +- `4dc4eda` Lib: remove unused assignment. +- `45302cf` Lib: remove range-check of channel-ids. + + +## Changes since v0.4.1 (a.k.a., v0.5.0): +### Major changes: +- Add amqp_get_broker_properties() function 5c7c40adc1 +- Remove distro-specific packaging a5749657ee +- Add -x flag to amqp-consume utilty 1d9c5291ff +- Add amqp_basic_nack() public API 9b168776fb +- Add delivery mode constants to amqp.h 5f291ea772 +- Add support for connection.blocked/connection.unblocked methods ccbc24d270 + +### Bug fixes: +- `f8c6cee749` Examples: Destroy amqp_envelope_t in consumer example +- `ac88db56d3` CMake: fix generation of librabbitmq.pc +- `d5b35afa40` CMake: fix missing POPT_INCLUDE_DIRS variable in tools/ +- `5ea6a0945a` build: provide independent locations for x64 libs +- `fac34656c0` Doc: documentation fixes +- `715901d675` Lib: Correct OpenSSL initialization under threaded conditions +- `ce64e57df8` Examples: Handle unexpected frames in amqp_consumer.c +- `bcda3e933d` CMake: Use GnuInstallDirs to generate install dirs +- `27245a4e15` Lib: correctly handle amqp_get_monotonic_timestamp on win32 +- `693662ef5b` Tools: honor --persistent flag in publish utility +- `01d9c3ca60` Doc: improve documentation in amqp_ssl_socket functions +- `02d5c58ae4` autoconf: correct librabbitmq.pc generation +- `1f4e0cc48b` Doc: improve documentation in amqp_tcp_socket functions + +## Changes since v0.4.0: +### Major changes: +- Removed distro-specific packaging d285d01 + +### Bug fixes: +- `a642602` FIX: destroy amqp_envelop_t object in consumer example +- `860dd71` FIX: correct generation of librabbitmq.pc under CMake +- `bdda7ab` FIX: amqp_socket_close() should not be exported from shlib +- `24f4131` FIX: Use correct buf/len vars when re-starting send() + +## Changes since v0.3.0: +### New Features/Enhancements: +- `amqp_login_with_properties()` function to connect to a broker sending a + properties table to the broker 21b124e #101 +- SSL support (Mike Steinert) 473c865 #17 +- `amqp_simple_wait_frame_noblock()` function variant to wait for a frame + with a timeout f8cfc72 #119 +- Allow memory to be released on a per-channel basis with + `amqp_maybe_release_buffers_on_channel()` 4a2d899 #5 +- Support for AMQP heartbeats while blocking in `amqp_simple_wait_frame*()` + and `amqp_basic_publish()` daa0e66 aca5dc1 +- `amqp_socket_open_noblock()` for a non-blocking socket connection + (Bogdan Padalko) 6ad770d +- `amqp_table_clone()` to do a deep-copy of an amqp_table_t 08af83a +- Add option to listen to multiple keys in `amqp_consume` tool (Brian Hammond) e6c256d +- Add contributed OpenVMS build system 448ab68 +- Higher level APIs for consuming messages 33ebeed #8 +- Doxygen-based API documentation. +- Many improvements to error-handling and reporting + +### Bug Fixes: +- `24ffaf8` FIX: autotools was broken when dependency-tracking was disabled +- `38e741b` FIX: CMake XmlTo not found warning +- `906f04f` FIX: htonll redeclared on Win32 v8 +- `8e41603` FIX: SIGPIPE not disabled on OS X/BSD #102 +- `872ea49` FIX: Header issues with amqp.h on Mingw on Win32 (yoniyoni) +- `0f1f75b` FIX: potential memory leak in amqp_new_connection +- `c9f6312` FIX: missing va_end in `amqp_login()`/`amqp_login_with_properties()` +- `7bb64e4` FIX: include amqp_tcp_socket.h in dpkg (Tim Stewart) +- `ba9d1f5` FIX: Report out of buffer space in `amqp_table_encode()` +- `9496e10` FIX: Remove `abort()` on invalid parameter in `amqp_send_frame()` +- `f209420` FIX: Remote `abort()` in `amqp_simple_wait_method()` +- `f027518` FIX: Return error on socket lib init error +- `0ae534a` FIX: Correctly handle 0 return val from `SSL_read()`/`SSL_write()` +- `22e41b8` FIX: Improve error handling in socket functions +- `33c2353` FIX: Set state->socket to NULL after `amqp_socket_close()` +- `c83e728` FIX: Incorrect error code returned +- `1a19879` FIX: redecl of int i in `amqp_tcp_socket_writev()` +- `7477449` FIX: incorrect bit-shift in `amqp_error_string2()` +- `2e37bb3` FIX: correctly handle `amqp_get_sockfd()` in `amqp_simple_wait_frame()` +- `52a459b` FIX: Don't delete state in `amqp_tune_connection()` on error +- `01e38dd` FIX: Correctly handle `mach_timebase_info()` failure +- `34bffb7` FIX: Correctly disable `SIGPIPE` on platforms with `SO_NOSIGPIPE` +- `3866433` FIX: Use correct number of bits in timer precision on MacOSX +- `b6a1dfe` FIX: Squash OpenSSL deprecated warnings on MacOSX (Bogdan Padalko) +- `7a217d5` FIX: Incorrect `assert()` in `wait_frame_inner()` +- `7942af3` FIX: Correctly handle 0-length table in `amqp_table_clone()` +- `157788e` FIX: Correctly handle 0-length strings in `amqp_basic_properties_clone()` +- `4eaf771` FIX: Correctly handle 0-length message body in `amqp_read_message()` +- `59f943b` FIX: Double-free SSL on connection failure +- `7a451a4` FIX: `amqp_open_socket()` not defined diff --git a/ext/librabbitmq/LICENSE-MIT b/ext/librabbitmq/LICENSE-MIT new file mode 100644 index 000000000..5c7630d8b --- /dev/null +++ b/ext/librabbitmq/LICENSE-MIT @@ -0,0 +1,28 @@ +Portions created by Alan Antonuk are Copyright (c) 2012-2013 +Alan Antonuk. All Rights Reserved. + +Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. +All Rights Reserved. + +Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 +VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ext/librabbitmq/README.md b/ext/librabbitmq/README.md new file mode 100644 index 000000000..525531581 --- /dev/null +++ b/ext/librabbitmq/README.md @@ -0,0 +1,128 @@ +# RabbitMQ C AMQP client library + +[![Build Status](https://secure.travis-ci.org/alanxz/rabbitmq-c.png?branch=master)](http://travis-ci.org/alanxz/rabbitmq-c) + +[![Coverage Status](https://coveralls.io/repos/github/alanxz/rabbitmq-c/badge.svg?branch=master)](https://coveralls.io/github/alanxz/rabbitmq-c?branch=master) + +## Introduction + +This is a C-language AMQP client library for use with v2.0+ of the +[RabbitMQ](http://www.rabbitmq.com/) broker. + + - + +Announcements regarding the library are periodically made on the +rabbitmq-c-users and cross-posted to rabbitmq-users. + + - + - + +## Latest Stable Version + +The latest stable release of rabbitmq-c can be found at: + + - + +## Documentation + +API documentation for v0.8.0+ can viewed from: + + + +## Getting started + +### Building and installing + +#### Prereqs: +- [CMake v2.6 or better](http://www.cmake.org/) +- A C compiler (GCC 4.4+, clang, and MSVC are test. Other compilers may also + work) +- *Optionally* [OpenSSL](http://www.openssl.org/) v0.9.8+ to enable support for + connecting to RabbitMQ over SSL/TLS +- *Optionally* [POpt](http://freecode.com/projects/popt) to build some handy + command-line tools. +- *Optionally* [XmlTo](https://fedorahosted.org/xmlto/) to build man pages for + the handy command-line tools +- *Optionally* [Doxygen](http://www.stack.nl/~dimitri/doxygen/) to build + developer API documentation. + +After downloading and extracting the source from a tarball to a directory +([see above](#latest-stable-version)), the commands to build rabbitmq-c on most +systems are: + + mkdir build && cd build + cmake .. + cmake --build [--config Release] . + +The --config Release flag should be used in multi-configuration generators e.g., +Visual Studio or XCode. + +It is also possible to point the CMake GUI tool at the CMakeLists.txt in the root of +the source tree and generate build projects or IDE workspace + +Installing the library and optionally specifying a prefix can be done with: + + cmake -DCMAKE_INSTALL_PREFIX=/usr/local .. + cmake --build . [--config Release] --target install + +More information on CMake can be found on its FAQ (http://www.cmake.org/Wiki/CMake_FAQ) + +Other interesting flags that can be passed to CMake: + +* `BUILD_EXAMPLES=ON/OFF` toggles building the examples. ON by default. +* `BUILD_SHARED_LIBS=ON/OFF` toggles building rabbitmq-c as a shared library. + ON by default. +* `BUILD_STATIC_LIBS=ON/OFF` toggles building rabbitmq-c as a static library. + OFF by default. +* `BUILD_TESTS=ON/OFF` toggles building test code. ON by default. +* `BUILD_TOOLS=ON/OFF` toggles building the command line tools. By default + this is ON if the build system can find the POpt header and library. +* `BUILD_TOOLS_DOCS=ON/OFF` toggles building the man pages for the command line + tools. By default this is ON if BUILD_TOOLS is ON and the build system can + find the XmlTo utility. +* `ENABLE_SSL_SUPPORT=ON/OFF` toggles building rabbitmq-c with SSL support. By + default this is ON if the OpenSSL headers and library can be found. +* `BUILD_API_DOCS=ON/OFF` - toggles building the Doxygen API documentation, by + default this is OFF + +## Running the examples + +Arrange for a RabbitMQ or other AMQP server to be running on +`localhost` at TCP port number 5672. + +In one terminal, run + + ./examples/amqp_listen localhost 5672 amq.direct test + +In another terminal, + + ./examples/amqp_sendstring localhost 5672 amq.direct test "hello world" + +You should see output similar to the following in the listener's +terminal window: + + Delivery 1, exchange amq.direct routingkey test + Content-type: text/plain + ---- + 00000000: 68 65 6C 6C 6F 20 77 6F : 72 6C 64 hello world + 0000000B: + +## Writing applications using `librabbitmq` + +Please see the `examples` directory for short examples of the use of +the `librabbitmq` library. + +### Threading + +You cannot share a socket, an `amqp_connection_state_t`, or a channel +between threads using `librabbitmq`. The `librabbitmq` library is +built with event-driven, single-threaded applications in mind, and +does not yet cater to any of the requirements of `pthread`ed +applications. + +Your applications instead should open an AMQP connection (and an +associated socket, of course) per thread. If your program needs to +access an AMQP connection or any of its channels from more than one +thread, it is entirely responsible for designing and implementing an +appropriate locking scheme. It will generally be much simpler to have +a connection exclusive to each thread that needs AMQP service. diff --git a/ext/librabbitmq/THANKS b/ext/librabbitmq/THANKS new file mode 100644 index 000000000..1f378eadf --- /dev/null +++ b/ext/librabbitmq/THANKS @@ -0,0 +1,8 @@ +Thank-you to the following people for their contributions to the +codebase: + + - Scott Brooks / Epic Advertising + + - Frank Gönninger + + - Daniel Schauenberg diff --git a/ext/librabbitmq/TODO b/ext/librabbitmq/TODO new file mode 100644 index 000000000..179d297aa --- /dev/null +++ b/ext/librabbitmq/TODO @@ -0,0 +1,9 @@ +Deal with version-mismatch-header received from the server + +Cope with unknown frame types better. Currently it gets horribly +confused about frame lengths. + +Make client brutal by default, killing the program on any amqp +error. Only if the user disables this behaviour will the user get to +deal with error conditions themselves. Make use of amqp_rpc_reply +consistent (i.e. universal), and rename it something like amqp_errno. diff --git a/ext/librabbitmq/appveyor.yml b/ext/librabbitmq/appveyor.yml new file mode 100644 index 000000000..0ef27f907 --- /dev/null +++ b/ext/librabbitmq/appveyor.yml @@ -0,0 +1,34 @@ +# appveyor configuration +version: '{build}' + +# Limit history cloned. This matches what travis-CI currently does. +clone_depth: 50 + +environment: + matrix: + - GENERATOR: Visual Studio 12 Win64 + BITS: 64 + - GENERATOR: Visual Studio 12 + BITS: 32 + +cache: + - c:\deps -> appveyor.yml + +# borrowed from https://github.com/FreeTDS/freetds +install: + # xidel (xpath command line tool) + - appveyor DownloadFile "https://downloads.sourceforge.net/project/videlibri/Xidel/Xidel 0.9.6/xidel-0.9.6.win32.zip" + - 7z x xidel-0.9.6.win32.zip xidel.exe + # detect version of Windows OpenSSL binaries published by the Shining Light Productions crew + - xidel https://slproweb.com/products/Win32OpenSSL.html --extract "(//td/a[starts-with(@href, '/download') and starts-with(text(), 'Win32 OpenSSL') and ends-with(text(), 'Light')])[1]/translate(substring-before(substring-after(text(), 'Win32 OpenSSL v'), ' Light'), '.', '_')" > openssl_ver.txt + - set /P OPENSSL_VER=< openssl_ver.txt + # OpenSSL + - appveyor DownloadFile https://slproweb.com/download/Win%BITS%OpenSSL-%OPENSSL_VER%.exe + - "Win%BITS%OpenSSL-%OPENSSL_VER%.exe /SP- /SILENT /SUPPRESSMSGBOXES /NORESTART" + +before_build: + - cmake -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_TESTS=ON -DENABLE_SSL_SUPPORT=True -G"%GENERATOR%" . + +build: + project: ALL_BUILD.vcxproj + verbosity: normal diff --git a/ext/librabbitmq/centos_x64/lib/librabbitmq.a b/ext/librabbitmq/centos_x64/lib/librabbitmq.a deleted file mode 100644 index d5c3e8b42..000000000 Binary files a/ext/librabbitmq/centos_x64/lib/librabbitmq.a and /dev/null differ diff --git a/ext/librabbitmq/cmake/CMakePushCheckState.cmake b/ext/librabbitmq/cmake/CMakePushCheckState.cmake new file mode 100644 index 000000000..038319b40 --- /dev/null +++ b/ext/librabbitmq/cmake/CMakePushCheckState.cmake @@ -0,0 +1,103 @@ +# This module defines two macros: +# CMAKE_PUSH_CHECK_STATE() +# and +# CMAKE_POP_CHECK_STATE() +# These two macros can be used to save and restore the state of the variables +# CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_LIBRARIES +# and CMAKE_REQUIRED_INCLUDES used by the various Check-files coming with CMake, +# like e.g. check_function_exists() etc. +# The variable contents are pushed on a stack, pushing multiple times is supported. +# This is useful e.g. when executing such tests in a Find-module, where they have to be set, +# but after the Find-module has been executed they should have the same value +# as they had before. +# +# Usage: +# cmake_push_check_state() +# set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF) +# check_function_exists(...) +# cmake_pop_check_state() + +#============================================================================= +# Copyright 2006-2011 Alexander Neundorf, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# ------------------------------------------------------------------------------ +# +# The above copyright and license notice applies to distributions of +# CMake in source and binary form. Some source files contain additional +# notices of original copyright by their contributors; see each source +# for details. Third-party software packages supplied with CMake under +# compatible licenses provide their own copyright notices documented in +# corresponding subdirectories. +# +# ------------------------------------------------------------------------------ +# +# CMake was initially developed by Kitware with the following sponsorship: +# +# * National Library of Medicine at the National Institutes of Health +# as part of the Insight Segmentation and Registration Toolkit (ITK). +# +# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel +# Visualization Initiative. +# +# * National Alliance for Medical Image Computing (NAMIC) is funded by the +# National Institutes of Health through the NIH Roadmap for Medical Research, +# Grant U54 EB005149. +# +# * Kitware, Inc. + +macro(CMAKE_PUSH_CHECK_STATE) + + if(NOT DEFINED _CMAKE_PUSH_CHECK_STATE_COUNTER) + set(_CMAKE_PUSH_CHECK_STATE_COUNTER 0) + endif() + + math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1") + + set(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_INCLUDES}) + set(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS}) + set(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LIBRARIES}) + set(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_FLAGS}) +endmacro() + +macro(CMAKE_POP_CHECK_STATE) + +# don't pop more than we pushed + if("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0") + + set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_FLAGS ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + + math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1") + endif() + +endmacro() diff --git a/ext/librabbitmq/cmake/COPYING-CMAKE-SCRIPTS b/ext/librabbitmq/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 000000000..53b6b71eb --- /dev/null +++ b/ext/librabbitmq/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/librabbitmq/cmake/FindPOPT.cmake b/ext/librabbitmq/cmake/FindPOPT.cmake new file mode 100644 index 000000000..79caa0189 --- /dev/null +++ b/ext/librabbitmq/cmake/FindPOPT.cmake @@ -0,0 +1,39 @@ +# - Try to find the popt options processing library +# The module will set the following variables +# +# POPT_FOUND - System has popt +# POPT_INCLUDE_DIR - The popt include directory +# POPT_LIBRARY - The libraries needed to use popt + +# use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_search_module(PC_POPT QUIET popt) +endif () + +# Find the include directories +FIND_PATH(POPT_INCLUDE_DIR + NAMES popt.h + HINTS + ${PC_POPT_INCLUDEDIR} + ${PC_POPT_INCLUDE_DIRS} + DOC "Path containing the popt.h include file" + ) + +FIND_LIBRARY(POPT_LIBRARY + NAMES popt + HINTS + ${PC_POPT_LIBRARYDIR} + ${PC_POPT_LIBRARY_DIRS} + DOC "popt library path" + ) + +include(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(POPT + REQUIRED_VARS POPT_INCLUDE_DIR POPT_LIBRARY + VERSION_VAR PC_POPT_VERSION) + +MARK_AS_ADVANCED(POPT_INCLUDE_DIR POPT_LIBRARY) diff --git a/ext/librabbitmq/cmake/FindXmlTo.cmake b/ext/librabbitmq/cmake/FindXmlTo.cmake new file mode 100644 index 000000000..d2d4d632b --- /dev/null +++ b/ext/librabbitmq/cmake/FindXmlTo.cmake @@ -0,0 +1,98 @@ +# - Convert XML docBook files to various formats +# This will convert XML docBook files to various formats like: +# man html txt dvi ps pdf +# macro XMLTO(outfiles infiles... MODES modes...) + +find_program ( XMLTO_EXECUTABLE + NAMES xmlto + DOC "path to the xmlto docbook xslt frontend" +) + + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(XMLTO + REQUIRED_VARS XMLTO_EXECUTABLE) + +mark_as_advanced( XMLTO_EXECUTABLE ) + +macro ( _XMLTO_FILE outfiles mode) + #special settings + set ( XMLTO_FILEEXT_man 1 ) + set ( XMLTO_MODE_html xhtml-nochunks ) + + if ( NOT XMLTO_MODE_${mode}) + set ( XMLTO_MODE_${mode} ${mode} ) + endif ( NOT XMLTO_MODE_${mode} ) + if ( NOT XMLTO_FILEEXT_${mode} ) + set ( XMLTO_FILEEXT_${mode} ${mode} ) + endif ( NOT XMLTO_FILEEXT_${mode} ) + + foreach ( dbFile ${ARGN} ) + #TODO: set XMLTO_FILEEXT_man to value from + if ( "${mode}" STREQUAL "man" ) + file ( READ "${dbFile}" _DB_FILE_CONTENTS ) + string ( REGEX MATCH "[^<]*" XMLTO_FILEEXT_${mode} "${_DB_FILE_CONTENTS}" ) + string ( REGEX REPLACE "^" "" XMLTO_FILEEXT_${mode} "${XMLTO_FILEEXT_${mode}}" ) + string ( REGEX REPLACE "[[:space:]]" "" XMLTO_FILEEXT_${mode} "${XMLTO_FILEEXT_${mode}}" ) + endif ( "${mode}" STREQUAL "man" ) + + get_filename_component ( dbFilePath ${CMAKE_CURRENT_BINARY_DIR}/${dbFile} PATH ) + get_filename_component ( dbFileWE ${dbFile} NAME_WE ) + get_filename_component ( dbFileAbsWE ${dbFilePath}/${dbFileWE} ABSOLUTE ) + + add_custom_command ( + OUTPUT ${dbFileAbsWE}.${XMLTO_FILEEXT_${mode}} + COMMAND ${XMLTO_EXECUTABLE} ${XMLTO_COMMAND_ARGS} -o ${dbFilePath} + ${XMLTO_MODE_${mode}} "${CMAKE_CURRENT_SOURCE_DIR}/${dbFile}" + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${dbFile} + DEPENDS ${XMLTO_DEPENDS} + VERBATIM + ) + + set ( ${outfiles} + ${${outfiles}} + ${dbFileAbsWE}.${XMLTO_FILEEXT_${mode}} + ) + endforeach ( dbFile ) +endmacro ( _XMLTO_FILE outfiles ) + +macro ( XMLTO ) + set ( XMLTO_MODES ) + set ( XMLTO_FILES ) + set ( XMLTO_HAS_MODES false ) + set ( XMLTO_ADD_DEFAULT false ) + foreach ( arg ${ARGN} ) + if ( ${arg} STREQUAL "MODES" ) + set ( XMLTO_HAS_MODES true ) + elseif ( ${arg} STREQUAL "ALL" ) + set ( XMLTO_ADD_DEFAULT true ) + else ( ${arg} STREQUAL "MODES" ) + if ( XMLTO_HAS_MODES ) + set ( XMLTO_MODES ${XMLTO_MODES} ${arg} ) + else ( XMLTO_HAS_MODES ) + set ( XMLTO_FILES ${XMLTO_FILES} ${arg} ) + endif ( XMLTO_HAS_MODES ) + endif ( ${arg} STREQUAL "MODES" ) + endforeach ( arg ${ARGN} ) + if ( NOT XMLTO_MODES ) + set ( XMLTO_MODES html ) + endif ( NOT XMLTO_MODES ) + + foreach ( mode ${XMLTO_MODES} ) + _xmlto_file ( XMLTO_FILES_${mode} ${mode} ${XMLTO_FILES} ) + if ( XMLTO_ADD_DEFAULT ) + add_custom_target ( ${mode} ALL + DEPENDS ${XMLTO_FILES_${mode}} + VERBATIM + ) + else ( XMLTO_ADD_DEFAULT ) + add_custom_target ( ${mode} + DEPENDS ${XMLTO_FILES_${mode}} + VERBATIM + ) + endif ( XMLTO_ADD_DEFAULT ) + endforeach ( mode ) + + set ( XMLTO_MODES ) + set ( XMLTO_FILES ) +endmacro ( XMLTO ) diff --git a/ext/librabbitmq/cmake/GNUInstallDirs.cmake b/ext/librabbitmq/cmake/GNUInstallDirs.cmake new file mode 100644 index 000000000..c8d77c6a1 --- /dev/null +++ b/ext/librabbitmq/cmake/GNUInstallDirs.cmake @@ -0,0 +1,205 @@ +#.rst: +# GNUInstallDirs +# -------------- +# +# Define GNU standard installation directories +# +# Provides install directory variables as defined for GNU software: +# +# :: +# +# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html +# +# Inclusion of this module defines the following variables: +# +# :: +# +# CMAKE_INSTALL_ - destination for files of a given type +# CMAKE_INSTALL_FULL_ - corresponding absolute path +# +# where is one of: +# +# :: +# +# BINDIR - user executables (bin) +# SBINDIR - system admin executables (sbin) +# LIBEXECDIR - program executables (libexec) +# SYSCONFDIR - read-only single-machine data (etc) +# SHAREDSTATEDIR - modifiable architecture-independent data (com) +# LOCALSTATEDIR - modifiable single-machine data (var) +# LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) +# INCLUDEDIR - C header files (include) +# OLDINCLUDEDIR - C header files for non-gcc (/usr/include) +# DATAROOTDIR - read-only architecture-independent data root (share) +# DATADIR - read-only architecture-independent data (DATAROOTDIR) +# INFODIR - info documentation (DATAROOTDIR/info) +# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) +# MANDIR - man documentation (DATAROOTDIR/man) +# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) +# +# Each CMAKE_INSTALL_ value may be passed to the DESTINATION +# options of install() commands for the corresponding file type. If the +# includer does not define a value the above-shown default will be used +# and the value will appear in the cache for editing by the user. Each +# CMAKE_INSTALL_FULL_ value contains an absolute path constructed +# from the corresponding destination by prepending (if necessary) the +# value of CMAKE_INSTALL_PREFIX. + +#============================================================================= +# Copyright 2011 Nikita Krupen'ko +# Copyright 2011 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +# Installation directories +# +if(NOT DEFINED CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SBINDIR) + set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) + set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) + set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) + set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) + set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LIBDIR) + set(_LIBDIR_DEFAULT "lib") + # Override this default 'lib' with 'lib64' iff: + # - we are on Linux system but NOT cross-compiling + # - we are NOT on debian + # - we are on a 64 bits system + # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf + # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if + # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" + # See http://wiki.debian.org/Multiarch + if(CMAKE_SYSTEM_NAME MATCHES "Linux" + AND NOT CMAKE_CROSSCOMPILING) + if (EXISTS "/etc/debian_version") # is this a debian system ? + if(CMAKE_LIBRARY_ARCHITECTURE) + set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") + endif() + else() # not debian, rely on CMAKE_SIZEOF_VOID_P: + if(NOT DEFINED CMAKE_SIZEOF_VOID_P) + message(AUTHOR_WARNING + "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " + "Please enable at least one language before including GNUInstallDirs.") + else() + if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(_LIBDIR_DEFAULT "lib64") + endif() + endif() + endif() + endif() + set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") +endif() + +if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) + set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") +endif() + +if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) + set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") +endif() + +#----------------------------------------------------------------------------- +# Values whose defaults are relative to DATAROOTDIR. Store empty values in +# the cache and store the defaults in local variables if the cache values are +# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. + +if(NOT CMAKE_INSTALL_DATADIR) + set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") + set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") +endif() + +if(NOT CMAKE_INSTALL_INFODIR) + set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") + set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") +endif() + +if(NOT CMAKE_INSTALL_LOCALEDIR) + set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") + set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") +endif() + +if(NOT CMAKE_INSTALL_MANDIR) + set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") + set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") +endif() + +if(NOT CMAKE_INSTALL_DOCDIR) + set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") + set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") +endif() + +#----------------------------------------------------------------------------- + +mark_as_advanced( + CMAKE_INSTALL_BINDIR + CMAKE_INSTALL_SBINDIR + CMAKE_INSTALL_LIBEXECDIR + CMAKE_INSTALL_SYSCONFDIR + CMAKE_INSTALL_SHAREDSTATEDIR + CMAKE_INSTALL_LOCALSTATEDIR + CMAKE_INSTALL_LIBDIR + CMAKE_INSTALL_INCLUDEDIR + CMAKE_INSTALL_OLDINCLUDEDIR + CMAKE_INSTALL_DATAROOTDIR + CMAKE_INSTALL_DATADIR + CMAKE_INSTALL_INFODIR + CMAKE_INSTALL_LOCALEDIR + CMAKE_INSTALL_MANDIR + CMAKE_INSTALL_DOCDIR + ) + +# Result directories +# +foreach(dir + BINDIR + SBINDIR + LIBEXECDIR + SYSCONFDIR + SHAREDSTATEDIR + LOCALSTATEDIR + LIBDIR + INCLUDEDIR + OLDINCLUDEDIR + DATAROOTDIR + DATADIR + INFODIR + LOCALEDIR + MANDIR + DOCDIR + ) + if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) + set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") + else() + set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") + endif() +endforeach() diff --git a/ext/librabbitmq/cmake/TestCInline.cmake b/ext/librabbitmq/cmake/TestCInline.cmake new file mode 100644 index 000000000..634364687 --- /dev/null +++ b/ext/librabbitmq/cmake/TestCInline.cmake @@ -0,0 +1,28 @@ +#Inspired from http://www.cmake.org/Wiki/CMakeTestInline + +IF(NOT DEFINED C_INLINE_KEYWORD) + + SET(INLINE_TEST_SRC "/* Inspired by autoconf's c.m4 */ +static inline int static_foo() {return 0\;} +int main(int argc, char *argv[]){return 0\;} +") + + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CMakeTestCInline.c ${INLINE_TEST_SRC}) + + FOREACH(KEYWORD "inline" "__inline__" "__inline") + IF(NOT DEFINED C_INLINE) + TRY_COMPILE(C_HAS_${KEYWORD} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/CMakeTestCInline.c + COMPILE_DEFINITIONS "-Dinline=${KEYWORD}" + ) + IF(C_HAS_${KEYWORD}) + SET(C_INLINE ${KEYWORD}) + ENDIF(C_HAS_${KEYWORD}) + ENDIF(NOT DEFINED C_INLINE) + ENDFOREACH(KEYWORD) + + SET(C_INLINE_KEYWORD ${C_INLINE} CACHE INTERNAL "The keyword needed by the C compiler to inline a function" FORCE) + message(STATUS "Found C inline keyword: ${C_INLINE_KEYWORD}") + +ENDIF(NOT DEFINED C_INLINE_KEYWORD) diff --git a/ext/librabbitmq/cmake/config.h.in b/ext/librabbitmq/cmake/config.h.in new file mode 100644 index 000000000..297cf40b6 --- /dev/null +++ b/ext/librabbitmq/cmake/config.h.in @@ -0,0 +1,14 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifndef __cplusplus +# define inline ${C_INLINE_KEYWORD} +#endif + +#cmakedefine HAVE_SELECT + +#cmakedefine HAVE_POLL + +#define AMQ_PLATFORM "@CMAKE_SYSTEM@" + +#endif /* CONFIG_H */ diff --git a/ext/librabbitmq/coverity/model.c b/ext/librabbitmq/coverity/model.c new file mode 100644 index 000000000..dc1ad986c --- /dev/null +++ b/ext/librabbitmq/coverity/model.c @@ -0,0 +1,17 @@ +/* Functions to help coverity do static analysis on rabbitmq-c */ + +typedef struct { +} amqp_rpc_reply_t; + +/* librabbitmq/amqp_private.h */ +void amqp_abort(const char* fmt, ...) { __coverity_panic__(); } + +/* tools/common.h */ +void die(const char* fmt, ...) { __coverity_panic__(); } +void die_errno(int err, const char* fmt, ...) { __coverity_panic__(); } +void die_amqp_error(int err, const char* fmt, ...) { __coverity_panic__(); } +void die_rpc(amqp_rpc_reply_t r, const char* fmt, ...) { __coverity_panic__(); } + +/* examples/utils.h */ +void die_on_amqp_error(amqp_rpc_reply_t* r) { __coverity_panic__(); } +void die_on_error(int r) { __coverity_panic__(); } diff --git a/ext/librabbitmq/docs/Doxyfile.in b/ext/librabbitmq/docs/Doxyfile.in new file mode 100644 index 000000000..8b5da7026 --- /dev/null +++ b/ext/librabbitmq/docs/Doxyfile.in @@ -0,0 +1,317 @@ +# Doxyfile 1.8.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = rabbitmq-c +PROJECT_NUMBER = @VERSION@ +PROJECT_BRIEF = "C AMQP Client library for RabbitMQ" +PROJECT_LOGO = +OUTPUT_DIRECTORY = . +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = YES +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/README.md \ + @CMAKE_CURRENT_SOURCE_DIR@/ChangeLog.md \ + @CMAKE_CURRENT_SOURCE_DIR@/librabbitmq \ + @CMAKE_CURRENT_SOURCE_DIR@/docs +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h \ + *.md +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = amqp_private.h \ + config.h +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@ \ + @CMAKE_CURRENT_SOURCE_DIR@/examples +EXAMPLE_PATTERNS = *.c \ + *.md +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = amqp_ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://www.mathjax.org/mathjax +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/librabbitmq \ + @CMAKE_CURRENT_BINARY_DIR@/librabbitmq +INCLUDE_FILE_PATTERNS = +PREDEFINED = AMQP_BEGIN_DECLS= \ + AMQP_END_DECLS= \ + AMQP_PUBLIC_FUNCTION= \ + AMQP_PUBLIC_VARIABLE= \ + AMQP_CALL= \ + AMQP_DEPRECATED(x)=x +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/ext/librabbitmq/examples/CMakeLists.txt b/ext/librabbitmq/examples/CMakeLists.txt new file mode 100644 index 000000000..4ea8e93e4 --- /dev/null +++ b/ext/librabbitmq/examples/CMakeLists.txt @@ -0,0 +1,52 @@ +if (NOT BUILD_SHARED_LIBS) + add_definitions(-DAMQP_STATIC) +endif() + +include_directories(${LIBRABBITMQ_INCLUDE_DIRS}) + +if (WIN32) + set(PLATFORM_DIR win32) +else (WIN32) + set(PLATFORM_DIR unix) +endif (WIN32) + +set(COMMON_SRCS + utils.h + utils.c + ${PLATFORM_DIR}/platform_utils.c + ) + +add_executable(amqp_sendstring amqp_sendstring.c ${COMMON_SRCS}) +target_link_libraries(amqp_sendstring ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_rpc_sendstring_client amqp_rpc_sendstring_client.c ${COMMON_SRCS}) +target_link_libraries(amqp_rpc_sendstring_client ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_exchange_declare amqp_exchange_declare.c ${COMMON_SRCS}) +target_link_libraries(amqp_exchange_declare ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_listen amqp_listen.c ${COMMON_SRCS}) +target_link_libraries(amqp_listen ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_producer amqp_producer.c ${COMMON_SRCS}) +target_link_libraries(amqp_producer ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_connect_timeout amqp_connect_timeout.c ${COMMON_SRCS}) +target_link_libraries(amqp_connect_timeout ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_consumer amqp_consumer.c ${COMMON_SRCS}) +target_link_libraries(amqp_consumer ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_unbind amqp_unbind.c ${COMMON_SRCS}) +target_link_libraries(amqp_unbind ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_bind amqp_bind.c ${COMMON_SRCS}) +target_link_libraries(amqp_bind ${RMQ_LIBRARY_TARGET}) + +add_executable(amqp_listenq amqp_listenq.c ${COMMON_SRCS}) +target_link_libraries(amqp_listenq ${RMQ_LIBRARY_TARGET}) + +if (ENABLE_SSL_SUPPORT) +add_executable(amqp_ssl_connect amqp_ssl_connect.c ${COMMON_SRCS}) +target_link_libraries(amqp_ssl_connect ${RMQ_LIBRARY_TARGET}) +endif (ENABLE_SSL_SUPPORT) diff --git a/ext/librabbitmq/examples/amqp_bind.c b/ext/librabbitmq/examples/amqp_bind.c new file mode 100644 index 000000000..46371a414 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_bind.c @@ -0,0 +1,95 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *bindingkey; + char const *queue; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 6) { + fprintf(stderr, "Usage: amqp_bind host port exchange bindingkey queue\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + bindingkey = argv[4]; + queue = argv[5]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + amqp_queue_bind(conn, 1, amqp_cstring_bytes(queue), + amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey), + amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding"); + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_connect_timeout.c b/ext/librabbitmq/examples/amqp_connect_timeout.c new file mode 100644 index 000000000..21bd02e2a --- /dev/null +++ b/ext/librabbitmq/examples/amqp_connect_timeout.c @@ -0,0 +1,114 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by Bogdan Padalko are Copyright (c) 2013. + * Bogdan Padalko. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port; + amqp_socket_t *socket; + amqp_connection_state_t conn; + struct timeval tval; + struct timeval *tv; + + if (argc < 3) { + fprintf(stderr, + "Usage: amqp_connect_timeout host port [timeout_sec " + "[timeout_usec=0]]\n"); + return 1; + } + + if (argc > 3) { + tv = &tval; + + tv->tv_sec = atoi(argv[3]); + + if (argc > 4) { + tv->tv_usec = atoi(argv[4]); + } else { + tv->tv_usec = 0; + } + + } else { + tv = NULL; + } + + hostname = argv[1]; + port = atoi(argv[2]); + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + + if (!socket) { + die("creating TCP socket"); + } + + die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv), + "opening TCP socket"); + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + + printf("Done\n"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_consumer.c b/ext/librabbitmq/examples/amqp_consumer.c new file mode 100644 index 000000000..93c7a21b8 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_consumer.c @@ -0,0 +1,215 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "utils.h" + +#define SUMMARY_EVERY_US 1000000 + +static void run(amqp_connection_state_t conn) { + uint64_t start_time = now_microseconds(); + int received = 0; + int previous_received = 0; + uint64_t previous_report_time = start_time; + uint64_t next_summary_time = start_time + SUMMARY_EVERY_US; + + amqp_frame_t frame; + + uint64_t now; + + for (;;) { + amqp_rpc_reply_t ret; + amqp_envelope_t envelope; + + now = now_microseconds(); + if (now > next_summary_time) { + int countOverInterval = received - previous_received; + double intervalRate = + countOverInterval / ((now - previous_report_time) / 1000000.0); + printf("%d ms: Received %d - %d since last report (%d Hz)\n", + (int)(now - start_time) / 1000, received, countOverInterval, + (int)intervalRate); + + previous_received = received; + previous_report_time = now; + next_summary_time += SUMMARY_EVERY_US; + } + + amqp_maybe_release_buffers(conn); + ret = amqp_consume_message(conn, &envelope, NULL, 0); + + if (AMQP_RESPONSE_NORMAL != ret.reply_type) { + if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && + AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) { + if (AMQP_STATUS_OK != amqp_simple_wait_frame(conn, &frame)) { + return; + } + + if (AMQP_FRAME_METHOD == frame.frame_type) { + switch (frame.payload.method.id) { + case AMQP_BASIC_ACK_METHOD: + /* if we've turned publisher confirms on, and we've published a + * message here is a message being confirmed. + */ + break; + case AMQP_BASIC_RETURN_METHOD: + /* if a published message couldn't be routed and the mandatory + * flag was set this is what would be returned. The message then + * needs to be read. + */ + { + amqp_message_t message; + ret = amqp_read_message(conn, frame.channel, &message, 0); + if (AMQP_RESPONSE_NORMAL != ret.reply_type) { + return; + } + + amqp_destroy_message(&message); + } + + break; + + case AMQP_CHANNEL_CLOSE_METHOD: + /* a channel.close method happens when a channel exception occurs, + * this can happen by publishing to an exchange that doesn't exist + * for example. + * + * In this case you would need to open another channel redeclare + * any queues that were declared auto-delete, and restart any + * consumers that were attached to the previous channel. + */ + return; + + case AMQP_CONNECTION_CLOSE_METHOD: + /* a connection.close method happens when a connection exception + * occurs, this can happen by trying to use a channel that isn't + * open for example. + * + * In this case the whole connection must be restarted. + */ + return; + + default: + fprintf(stderr, "An unexpected method was received %u\n", + frame.payload.method.id); + return; + } + } + } + + } else { + amqp_destroy_envelope(&envelope); + } + + received++; + } +} + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *bindingkey; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + amqp_bytes_t queuename; + + if (argc < 3) { + fprintf(stderr, "Usage: amqp_consumer host port\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = "amq.direct"; /* argv[3]; */ + bindingkey = "test queue"; /* argv[4]; */ + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + { + amqp_queue_declare_ok_t *r = amqp_queue_declare( + conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue"); + queuename = amqp_bytes_malloc_dup(r->queue); + if (queuename.bytes == NULL) { + fprintf(stderr, "Out of memory while copying queue name"); + return 1; + } + } + + amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange), + amqp_cstring_bytes(bindingkey), amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue"); + + amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0, + amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming"); + + run(conn); + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_exchange_declare.c b/ext/librabbitmq/examples/amqp_exchange_declare.c new file mode 100644 index 000000000..2199a0b32 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_exchange_declare.c @@ -0,0 +1,94 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *exchangetype; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 5) { + fprintf(stderr, + "Usage: amqp_exchange_declare host port exchange exchangetype\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + exchangetype = argv[4]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + amqp_exchange_declare(conn, 1, amqp_cstring_bytes(exchange), + amqp_cstring_bytes(exchangetype), 0, 0, 0, 0, + amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring exchange"); + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_listen.c b/ext/librabbitmq/examples/amqp_listen.c new file mode 100644 index 000000000..902622667 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_listen.c @@ -0,0 +1,143 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *bindingkey; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + amqp_bytes_t queuename; + + if (argc < 5) { + fprintf(stderr, "Usage: amqp_listen host port exchange bindingkey\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + bindingkey = argv[4]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + { + amqp_queue_declare_ok_t *r = amqp_queue_declare( + conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue"); + queuename = amqp_bytes_malloc_dup(r->queue); + if (queuename.bytes == NULL) { + fprintf(stderr, "Out of memory while copying queue name"); + return 1; + } + } + + amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange), + amqp_cstring_bytes(bindingkey), amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue"); + + amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0, + amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming"); + + { + for (;;) { + amqp_rpc_reply_t res; + amqp_envelope_t envelope; + + amqp_maybe_release_buffers(conn); + + res = amqp_consume_message(conn, &envelope, NULL, 0); + + if (AMQP_RESPONSE_NORMAL != res.reply_type) { + break; + } + + printf("Delivery %u, exchange %.*s routingkey %.*s\n", + (unsigned)envelope.delivery_tag, (int)envelope.exchange.len, + (char *)envelope.exchange.bytes, (int)envelope.routing_key.len, + (char *)envelope.routing_key.bytes); + + if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + printf("Content-type: %.*s\n", + (int)envelope.message.properties.content_type.len, + (char *)envelope.message.properties.content_type.bytes); + } + printf("----\n"); + + amqp_dump(envelope.message.body.bytes, envelope.message.body.len); + + amqp_destroy_envelope(&envelope); + } + } + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_listenq.c b/ext/librabbitmq/examples/amqp_listenq.c new file mode 100644 index 000000000..624dc5ccc --- /dev/null +++ b/ext/librabbitmq/examples/amqp_listenq.c @@ -0,0 +1,122 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *queuename; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 4) { + fprintf(stderr, "Usage: amqp_listenq host port queuename\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + queuename = argv[3]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + amqp_basic_consume(conn, 1, amqp_cstring_bytes(queuename), amqp_empty_bytes, + 0, 0, 0, amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming"); + + for (;;) { + amqp_rpc_reply_t res; + amqp_envelope_t envelope; + + amqp_maybe_release_buffers(conn); + + res = amqp_consume_message(conn, &envelope, NULL, 0); + + if (AMQP_RESPONSE_NORMAL != res.reply_type) { + break; + } + + printf("Delivery %u, exchange %.*s routingkey %.*s\n", + (unsigned)envelope.delivery_tag, (int)envelope.exchange.len, + (char *)envelope.exchange.bytes, (int)envelope.routing_key.len, + (char *)envelope.routing_key.bytes); + + if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + printf("Content-type: %.*s\n", + (int)envelope.message.properties.content_type.len, + (char *)envelope.message.properties.content_type.bytes); + } + printf("----\n"); + + amqp_dump(envelope.message.body.bytes, envelope.message.body.len); + + amqp_destroy_envelope(&envelope); + } + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_producer.c b/ext/librabbitmq/examples/amqp_producer.c new file mode 100644 index 000000000..6e78fcb66 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_producer.c @@ -0,0 +1,150 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +#define SUMMARY_EVERY_US 1000000 + +static void send_batch(amqp_connection_state_t conn, char const *queue_name, + int rate_limit, int message_count) { + uint64_t start_time = now_microseconds(); + int i; + int sent = 0; + int previous_sent = 0; + uint64_t previous_report_time = start_time; + uint64_t next_summary_time = start_time + SUMMARY_EVERY_US; + + char message[256]; + amqp_bytes_t message_bytes; + + for (i = 0; i < (int)sizeof(message); i++) { + message[i] = i & 0xff; + } + + message_bytes.len = sizeof(message); + message_bytes.bytes = message; + + for (i = 0; i < message_count; i++) { + uint64_t now = now_microseconds(); + + die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes("amq.direct"), + amqp_cstring_bytes(queue_name), 0, 0, NULL, + message_bytes), + "Publishing"); + sent++; + if (now > next_summary_time) { + int countOverInterval = sent - previous_sent; + double intervalRate = + countOverInterval / ((now - previous_report_time) / 1000000.0); + printf("%d ms: Sent %d - %d since last report (%d Hz)\n", + (int)(now - start_time) / 1000, sent, countOverInterval, + (int)intervalRate); + + previous_sent = sent; + previous_report_time = now; + next_summary_time += SUMMARY_EVERY_US; + } + + while (((i * 1000000.0) / (now - start_time)) > rate_limit) { + microsleep(2000); + now = now_microseconds(); + } + } + + { + uint64_t stop_time = now_microseconds(); + int total_delta = (int)(stop_time - start_time); + + printf("PRODUCER - Message count: %d\n", message_count); + printf("Total time, milliseconds: %d\n", total_delta / 1000); + printf("Overall messages-per-second: %g\n", + (message_count / (total_delta / 1000000.0))); + } +} + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + int rate_limit; + int message_count; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 5) { + fprintf(stderr, + "Usage: amqp_producer host port rate_limit message_count\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + rate_limit = atoi(argv[3]); + message_count = atoi(argv[4]); + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + send_batch(conn, "test queue", rate_limit, message_count); + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_rpc_sendstring_client.c b/ext/librabbitmq/examples/amqp_rpc_sendstring_client.c new file mode 100644 index 000000000..59918e531 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_rpc_sendstring_client.c @@ -0,0 +1,243 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "utils.h" + +int main(int argc, char *argv[]) { + char const *hostname; + int port, status; + char const *exchange; + char const *routingkey; + char const *messagebody; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + amqp_bytes_t reply_to_queue; + + if (argc < 6) { /* minimum number of mandatory arguments */ + fprintf(stderr, + "usage:\namqp_rpc_sendstring_client host port exchange routingkey " + "messagebody\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + routingkey = argv[4]; + messagebody = argv[5]; + + /* + establish a channel that is used to connect RabbitMQ server + */ + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + /* + create private reply_to queue + */ + + { + amqp_queue_declare_ok_t *r = amqp_queue_declare( + conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue"); + reply_to_queue = amqp_bytes_malloc_dup(r->queue); + if (reply_to_queue.bytes == NULL) { + fprintf(stderr, "Out of memory while copying queue name"); + return 1; + } + } + + /* + send the message + */ + + { + /* + set properties + */ + amqp_basic_properties_t props; + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | + AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_REPLY_TO_FLAG | + AMQP_BASIC_CORRELATION_ID_FLAG; + props.content_type = amqp_cstring_bytes("text/plain"); + props.delivery_mode = 2; /* persistent delivery mode */ + props.reply_to = amqp_bytes_malloc_dup(reply_to_queue); + if (props.reply_to.bytes == NULL) { + fprintf(stderr, "Out of memory while copying queue name"); + return 1; + } + props.correlation_id = amqp_cstring_bytes("1"); + + /* + publish + */ + die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange), + amqp_cstring_bytes(routingkey), 0, 0, + &props, amqp_cstring_bytes(messagebody)), + "Publishing"); + + amqp_bytes_free(props.reply_to); + } + + /* + wait an answer + */ + + { + amqp_basic_consume(conn, 1, reply_to_queue, amqp_empty_bytes, 0, 1, 0, + amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming"); + amqp_bytes_free(reply_to_queue); + + { + amqp_frame_t frame; + int result; + + amqp_basic_deliver_t *d; + amqp_basic_properties_t *p; + size_t body_target; + size_t body_received; + + for (;;) { + amqp_maybe_release_buffers(conn); + result = amqp_simple_wait_frame(conn, &frame); + printf("Result: %d\n", result); + if (result < 0) { + break; + } + + printf("Frame type: %u channel: %u\n", frame.frame_type, frame.channel); + if (frame.frame_type != AMQP_FRAME_METHOD) { + continue; + } + + printf("Method: %s\n", amqp_method_name(frame.payload.method.id)); + if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) { + continue; + } + + d = (amqp_basic_deliver_t *)frame.payload.method.decoded; + printf("Delivery: %u exchange: %.*s routingkey: %.*s\n", + (unsigned)d->delivery_tag, (int)d->exchange.len, + (char *)d->exchange.bytes, (int)d->routing_key.len, + (char *)d->routing_key.bytes); + + result = amqp_simple_wait_frame(conn, &frame); + if (result < 0) { + break; + } + + if (frame.frame_type != AMQP_FRAME_HEADER) { + fprintf(stderr, "Expected header!"); + abort(); + } + p = (amqp_basic_properties_t *)frame.payload.properties.decoded; + if (p->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + printf("Content-type: %.*s\n", (int)p->content_type.len, + (char *)p->content_type.bytes); + } + printf("----\n"); + + body_target = (size_t)frame.payload.properties.body_size; + body_received = 0; + + while (body_received < body_target) { + result = amqp_simple_wait_frame(conn, &frame); + if (result < 0) { + break; + } + + if (frame.frame_type != AMQP_FRAME_BODY) { + fprintf(stderr, "Expected body!"); + abort(); + } + + body_received += frame.payload.body_fragment.len; + assert(body_received <= body_target); + + amqp_dump(frame.payload.body_fragment.bytes, + frame.payload.body_fragment.len); + } + + if (body_received != body_target) { + /* Can only happen when amqp_simple_wait_frame returns <= 0 */ + /* We break here to close the connection */ + break; + } + + /* everything was fine, we can quit now because we received the reply */ + break; + } + } + } + + /* + closing + */ + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_sendstring.c b/ext/librabbitmq/examples/amqp_sendstring.c new file mode 100644 index 000000000..75492aa36 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_sendstring.c @@ -0,0 +1,103 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *routingkey; + char const *messagebody; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 6) { + fprintf( + stderr, + "Usage: amqp_sendstring host port exchange routingkey messagebody\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + routingkey = argv[4]; + messagebody = argv[5]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + { + amqp_basic_properties_t props; + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG; + props.content_type = amqp_cstring_bytes("text/plain"); + props.delivery_mode = 2; /* persistent delivery mode */ + die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange), + amqp_cstring_bytes(routingkey), 0, 0, + &props, amqp_cstring_bytes(messagebody)), + "Publishing"); + } + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_ssl_connect.c b/ext/librabbitmq/examples/amqp_ssl_connect.c new file mode 100644 index 000000000..3674c3337 --- /dev/null +++ b/ext/librabbitmq/examples/amqp_ssl_connect.c @@ -0,0 +1,135 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by Mike Steinert are Copyright (c) 2012-2013 + * Mike Steinert. All Rights Reserved. + * + * Portions created by Bogdan Padalko are Copyright (c) 2013. + * Bogdan Padalko. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port; + int timeout; + amqp_socket_t *socket; + amqp_connection_state_t conn; + struct timeval tval; + struct timeval *tv; + + if (argc < 3) { + fprintf(stderr, + "Usage: amqps_connect_timeout host port timeout_sec " + "[cacert.pem [verifypeer] [verifyhostname] [key.pem cert.pem]]\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + + timeout = atoi(argv[3]); + if (timeout > 0) { + tv = &tval; + + tv->tv_sec = timeout; + tv->tv_usec = 0; + } else { + tv = NULL; + } + + conn = amqp_new_connection(); + + socket = amqp_ssl_socket_new(conn); + if (!socket) { + die("creating SSL/TLS socket"); + } + + amqp_ssl_socket_set_verify_peer(socket, 0); + amqp_ssl_socket_set_verify_hostname(socket, 0); + + if (argc > 5) { + int nextarg = 5; + die_on_error(amqp_ssl_socket_set_cacert(socket, argv[4]), + "setting CA certificate"); + if (argc > nextarg && !strcmp("verifypeer", argv[nextarg])) { + amqp_ssl_socket_set_verify_peer(socket, 1); + nextarg++; + } + if (argc > nextarg && !strcmp("verifyhostname", argv[nextarg])) { + amqp_ssl_socket_set_verify_hostname(socket, 1); + nextarg++; + } + if (argc > nextarg + 1) { + die_on_error( + amqp_ssl_socket_set_key(socket, argv[nextarg + 1], argv[nextarg]), + "setting client key"); + } + } + + die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv), + "opening SSL/TLS connection"); + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + die_on_error(amqp_uninitialize_ssl_library(), "Uninitializing SSL library"); + + printf("Done\n"); + return 0; +} diff --git a/ext/librabbitmq/examples/amqp_unbind.c b/ext/librabbitmq/examples/amqp_unbind.c new file mode 100644 index 000000000..aea07381a --- /dev/null +++ b/ext/librabbitmq/examples/amqp_unbind.c @@ -0,0 +1,95 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +int main(int argc, char const *const *argv) { + char const *hostname; + int port, status; + char const *exchange; + char const *bindingkey; + char const *queue; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if (argc < 6) { + fprintf(stderr, "Usage: amqp_unbind host port exchange bindingkey queue\n"); + return 1; + } + + hostname = argv[1]; + port = atoi(argv[2]); + exchange = argv[3]; + bindingkey = argv[4]; + queue = argv[5]; + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket"); + } + + status = amqp_socket_open(socket, hostname, port); + if (status) { + die("opening TCP socket"); + } + + die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, + "guest", "guest"), + "Logging in"); + amqp_channel_open(conn, 1); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel"); + + amqp_queue_unbind(conn, 1, amqp_cstring_bytes(queue), + amqp_cstring_bytes(exchange), + amqp_cstring_bytes(bindingkey), amqp_empty_table); + die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding"); + + die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), + "Closing channel"); + die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "Closing connection"); + die_on_error(amqp_destroy_connection(conn), "Ending connection"); + return 0; +} diff --git a/ext/librabbitmq/examples/unix/platform_utils.c b/ext/librabbitmq/examples/unix/platform_utils.c new file mode 100644 index 000000000..e420b823a --- /dev/null +++ b/ext/librabbitmq/examples/unix/platform_utils.c @@ -0,0 +1,52 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +uint64_t now_microseconds(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; +} + +void microsleep(int usec) { + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 1000 * usec; + nanosleep(&req, NULL); +} diff --git a/ext/librabbitmq/examples/utils.c b/ext/librabbitmq/examples/utils.c new file mode 100644 index 000000000..8d1b4c63c --- /dev/null +++ b/ext/librabbitmq/examples/utils.c @@ -0,0 +1,188 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" + +void die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +void die_on_error(int x, char const *context) { + if (x < 0) { + fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x)); + exit(1); + } +} + +void die_on_amqp_error(amqp_rpc_reply_t x, char const *context) { + switch (x.reply_type) { + case AMQP_RESPONSE_NORMAL: + return; + + case AMQP_RESPONSE_NONE: + fprintf(stderr, "%s: missing RPC reply type!\n", context); + break; + + case AMQP_RESPONSE_LIBRARY_EXCEPTION: + fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x.library_error)); + break; + + case AMQP_RESPONSE_SERVER_EXCEPTION: + switch (x.reply.id) { + case AMQP_CONNECTION_CLOSE_METHOD: { + amqp_connection_close_t *m = + (amqp_connection_close_t *)x.reply.decoded; + fprintf(stderr, "%s: server connection error %uh, message: %.*s\n", + context, m->reply_code, (int)m->reply_text.len, + (char *)m->reply_text.bytes); + break; + } + case AMQP_CHANNEL_CLOSE_METHOD: { + amqp_channel_close_t *m = (amqp_channel_close_t *)x.reply.decoded; + fprintf(stderr, "%s: server channel error %uh, message: %.*s\n", + context, m->reply_code, (int)m->reply_text.len, + (char *)m->reply_text.bytes); + break; + } + default: + fprintf(stderr, "%s: unknown server error, method id 0x%08X\n", + context, x.reply.id); + break; + } + break; + } + + exit(1); +} + +static void dump_row(long count, int numinrow, int *chs) { + int i; + + printf("%08lX:", count - numinrow); + + if (numinrow > 0) { + for (i = 0; i < numinrow; i++) { + if (i == 8) { + printf(" :"); + } + printf(" %02X", chs[i]); + } + for (i = numinrow; i < 16; i++) { + if (i == 8) { + printf(" :"); + } + printf(" "); + } + printf(" "); + for (i = 0; i < numinrow; i++) { + if (isprint(chs[i])) { + printf("%c", chs[i]); + } else { + printf("."); + } + } + } + printf("\n"); +} + +static int rows_eq(int *a, int *b) { + int i; + + for (i = 0; i < 16; i++) + if (a[i] != b[i]) { + return 0; + } + + return 1; +} + +void amqp_dump(void const *buffer, size_t len) { + unsigned char *buf = (unsigned char *)buffer; + long count = 0; + int numinrow = 0; + int chs[16]; + int oldchs[16] = {0}; + int showed_dots = 0; + size_t i; + + for (i = 0; i < len; i++) { + int ch = buf[i]; + + if (numinrow == 16) { + int j; + + if (rows_eq(oldchs, chs)) { + if (!showed_dots) { + showed_dots = 1; + printf( + " .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n"); + } + } else { + showed_dots = 0; + dump_row(count, numinrow, chs); + } + + for (j = 0; j < 16; j++) { + oldchs[j] = chs[j]; + } + + numinrow = 0; + } + + count++; + chs[numinrow++] = ch; + } + + dump_row(count, numinrow, chs); + + if (numinrow != 0) { + printf("%08lX:\n", count); + } +} diff --git a/ext/librabbitmq/examples/utils.h b/ext/librabbitmq/examples/utils.h new file mode 100644 index 000000000..0fa7392ab --- /dev/null +++ b/ext/librabbitmq/examples/utils.h @@ -0,0 +1,48 @@ +#ifndef librabbitmq_examples_utils_h +#define librabbitmq_examples_utils_h + +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +void die(const char *fmt, ...); +extern void die_on_error(int x, char const *context); +extern void die_on_amqp_error(amqp_rpc_reply_t x, char const *context); + +extern void amqp_dump(void const *buffer, size_t len); + +extern uint64_t now_microseconds(void); +extern void microsleep(int usec); + +#endif diff --git a/ext/librabbitmq/examples/win32/platform_utils.c b/ext/librabbitmq/examples/win32/platform_utils.c new file mode 100644 index 000000000..49fd377e3 --- /dev/null +++ b/ext/librabbitmq/examples/win32/platform_utils.c @@ -0,0 +1,47 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include + +#include + +uint64_t now_microseconds(void) { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime) / + 10; +} + +void microsleep(int usec) { Sleep(usec / 1000); } diff --git a/ext/librabbitmq/librabbitmq.pc.in b/ext/librabbitmq/librabbitmq.pc.in new file mode 100644 index 000000000..17c1e0835 --- /dev/null +++ b/ext/librabbitmq/librabbitmq.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: rabbitmq-c +Description: An AMQP 0-9-1 client library +Version: @VERSION@ +URL: https://github.com/alanxz/rabbitmq-c +Requires.private: @requires_private@ +Libs: -L${libdir} -lrabbitmq +Libs.private: @libs_private@ +CFlags: -I${includedir} diff --git a/ext/librabbitmq/librabbitmq/CMakeLists.txt b/ext/librabbitmq/librabbitmq/CMakeLists.txt new file mode 100644 index 000000000..bd5369a8f --- /dev/null +++ b/ext/librabbitmq/librabbitmq/CMakeLists.txt @@ -0,0 +1,186 @@ +project(librabbitmq "C") + +if (REGENERATE_AMQP_FRAMING) + set(AMQP_CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/amqp_codegen.py") + set(CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/codegen.py") + set(AMQP_SPEC_JSON_PATH "${AMQP_CODEGEN_DIR}/amqp-rabbitmq-0.9.1.json") + set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.h) + set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.c) + + if (PYTHON_VERSION_MAJOR GREATER 2) + set(CONVERT_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${CODEGEN_PY} > codegen_2to3.out) + set(CONVERT_AMQP_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${AMQP_CODEGEN_PY} > amqp_codegen_2to3.out) + else () + set(CONVERT_CODEGEN "") + set(CONVERT_AMQP_CODEGEN "") + endif () + + add_custom_command( + OUTPUT ${CODEGEN_PY} + COMMAND ${CMAKE_COMMAND} ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py ${CODEGEN_PY} + COMMAND ${CONVERT_CODEGEN} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py + VERBATIM) + + add_custom_command( + OUTPUT ${AMQP_CODEGEN_PY} + COMMAND ${CMAKE_COMMAND} ARGS -E copy ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_PY} + COMMAND ${CONVERT_AMQP_CODEGEN} + DEPENDS ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_TARGET} + VERBATIM) + + add_custom_command( + OUTPUT ${AMQP_FRAMING_H_PATH} + COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} header ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_H_PATH} + DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY} + VERBATIM) + + add_custom_command( + OUTPUT ${AMQP_FRAMING_C_PATH} + COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} body ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_C_PATH} + DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY} + VERBATIM) +else (REGENERATE_AMQP_FRAMING) + set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.h) + set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.c) +endif (REGENERATE_AMQP_FRAMING) + +if(WIN32) + set(SOCKET_IMPL "win32") +else(WIN32) + set(SOCKET_IMPL "unix") +endif(WIN32) + +if(MSVC) + if(MSVC_VERSION LESS 1600) + set(MSINTTYPES_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes") + set(STDINT_H_INSTALL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes/stdint.h") + endif(MSVC_VERSION LESS 1600) +endif(MSVC) + +# NOTE: order is important here: if we generate amqp_framing.h/.c it'll be in the +# binary directory, and should shadow whats in the source directory +set(LIBRABBITMQ_INCLUDE_DIRS + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${SOCKET_IMPL} + ${MSINTTYPES_INCLUDE} + ) + +include_directories(${LIBRABBITMQ_INCLUDE_DIRS}) + +set(LIBRABBITMQ_INCLUDE_DIRS + ${LIBRABBITMQ_INCLUDE_DIRS} + PARENT_SCOPE) + +add_definitions(-DHAVE_CONFIG_H) + +if (ENABLE_SSL_SUPPORT) + add_definitions(-DWITH_SSL=1) + set(AMQP_SSL_SOCKET_H_PATH amqp_ssl_socket.h) + + set(AMQP_SSL_SRCS ${AMQP_SSL_SOCKET_H_PATH} + amqp_openssl.c + amqp_openssl_hostname_validation.c + amqp_openssl_hostname_validation.h + amqp_hostcheck.c + amqp_hostcheck.h + amqp_openssl_bio.c + amqp_openssl_bio.h + ) + include_directories(${OPENSSL_INCLUDE_DIR}) + set(AMQP_SSL_LIBS ${OPENSSL_LIBRARIES}) + if (APPLE) + # Apple has deprecated OpenSSL in 10.7+. This disables that warning. + set_source_files_properties(${AMQP_SSL_SRCS} + PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) + endif() + + if (WIN32) + set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} win32/threads.h win32/threads.c) + else() + set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} unix/threads.h) + endif() +endif() + +set(RABBITMQ_SOURCES + ${AMQP_FRAMING_H_PATH} + ${AMQP_FRAMING_C_PATH} + amqp_api.c amqp.h amqp_connection.c amqp_mem.c amqp_private.h amqp_socket.c + amqp_table.c amqp_url.c amqp_socket.h amqp_tcp_socket.c amqp_tcp_socket.h + amqp_time.c amqp_time.h + amqp_consumer.c + ${AMQP_SSL_SRCS} +) + +add_definitions(-DAMQP_BUILD) + +set(RMQ_LIBRARIES ${AMQP_SSL_LIBS} ${SOCKET_LIBRARIES} ${LIBRT} ${CMAKE_THREAD_LIBS_INIT}) + +if (BUILD_SHARED_LIBS) + add_library(rabbitmq SHARED ${RABBITMQ_SOURCES}) + if (THREADS_HAVE_PTHREAD_ARG) + target_compile_options(rabbitmq PUBLIC "-pthread") + endif() + + target_link_libraries(rabbitmq ${RMQ_LIBRARIES}) + + if (WIN32) + set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} OUTPUT_NAME rabbitmq.${RMQ_SOVERSION}) + else (WIN32) + set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION}) + endif (WIN32) + + install(TARGETS rabbitmq + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + set(RMQ_LIBRARY_TARGET rabbitmq) +endif (BUILD_SHARED_LIBS) + +if (BUILD_STATIC_LIBS) + add_library(rabbitmq-static STATIC ${RABBITMQ_SOURCES}) + if (THREADS_HAVE_PTHREAD_ARG) + target_compile_options(rabbitmq-static PUBLIC "-pthread") + endif() + + target_link_libraries(rabbitmq-static ${RMQ_LIBRARIES}) + + set_target_properties(rabbitmq-static PROPERTIES COMPILE_DEFINITIONS AMQP_STATIC) + if (WIN32) + set_target_properties(rabbitmq-static PROPERTIES + VERSION ${RMQ_VERSION} + OUTPUT_NAME librabbitmq.${RMQ_SOVERSION}) + + if(MSVC) + set_target_properties(rabbitmq-static PROPERTIES + # Embed debugging info in the library itself instead of generating + # a .pdb file. + COMPILE_OPTIONS "/Z7") + endif(MSVC) + + else (WIN32) + set_target_properties(rabbitmq-static PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION} OUTPUT_NAME rabbitmq) + endif (WIN32) + + install(TARGETS rabbitmq-static + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + if (NOT DEFINED RMQ_LIBRARY_TARGET) + set(RMQ_LIBRARY_TARGET rabbitmq-static) + endif () +endif (BUILD_STATIC_LIBS) + +install(FILES + amqp.h + ${AMQP_FRAMING_H_PATH} + amqp_tcp_socket.h + ${AMQP_SSL_SOCKET_H_PATH} + ${STDINT_H_INSTALL_FILE} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + +set(RMQ_LIBRARY_TARGET ${RMQ_LIBRARY_TARGET} PARENT_SCOPE) diff --git a/ext/librabbitmq/centos_x64/include/amqp.h b/ext/librabbitmq/librabbitmq/amqp.h similarity index 99% rename from ext/librabbitmq/centos_x64/include/amqp.h rename to ext/librabbitmq/librabbitmq/amqp.h index 2983b1665..734abb8d9 100644 --- a/ext/librabbitmq/centos_x64/include/amqp.h +++ b/ext/librabbitmq/librabbitmq/amqp.h @@ -219,9 +219,9 @@ AMQP_BEGIN_DECLS */ #define AMQP_VERSION_MAJOR 0 -#define AMQP_VERSION_MINOR 10 +#define AMQP_VERSION_MINOR 9 #define AMQP_VERSION_PATCH 0 -#define AMQP_VERSION_IS_RELEASE 0 +#define AMQP_VERSION_IS_RELEASE 1 /** * \def AMQP_VERSION_CODE @@ -328,16 +328,13 @@ char const *AMQP_CALL amqp_version(void); /** * \def AMQP_DEFAULT_MAX_CHANNELS * - * Default maximum number of channels (2047, RabbitMQ default limit of 2048, - * minus 1 for channel 0). RabbitMQ set a default limit of 2048 channels per - * connection in v3.7.5 to prevent broken clients from leaking too many - * channels. + * Default maximum number of channels (0, no limit) * * \sa amqp_login(), amqp_login_with_properties() * * \since v0.4.0 */ -#define AMQP_DEFAULT_MAX_CHANNELS 2047 +#define AMQP_DEFAULT_MAX_CHANNELS 0 /** * \def AMQP_DEFAULT_HEARTBEAT diff --git a/ext/librabbitmq/librabbitmq/amqp_api.c b/ext/librabbitmq/librabbitmq/amqp_api.c new file mode 100644 index 000000000..28b238476 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_api.c @@ -0,0 +1,394 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +/* MSVC complains about sprintf being deprecated in favor of sprintf_s */ +#define _CRT_SECURE_NO_WARNINGS +/* MSVC complains about strdup being deprecated in favor of _strdup */ +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include "amqp_private.h" +#include "amqp_time.h" +#include +#include +#include +#include +#include + +#define ERROR_MASK (0x00FF) +#define ERROR_CATEGORY_MASK (0xFF00) + +enum error_category_enum_ { EC_base = 0, EC_tcp = 1, EC_ssl = 2 }; + +static const char *base_error_strings[] = { + /* AMQP_STATUS_OK 0x0 */ + "operation completed successfully", + /* AMQP_STATUS_NO_MEMORY -0x0001 */ + "could not allocate memory", + /* AMQP_STATUS_BAD_AQMP_DATA -0x0002 */ + "invalid AMQP data", + /* AMQP_STATUS_UNKNOWN_CLASS -0x0003 */ + "unknown AMQP class id", + /* AMQP_STATUS_UNKNOWN_METHOD -0x0004 */ + "unknown AMQP method id", + /* AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED -0x0005 */ + "hostname lookup failed", + /* AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION -0x0006 */ + "incompatible AMQP version", + /* AMQP_STATUS_CONNECTION_CLOSED -0x0007 */ + "connection closed unexpectedly", + /* AMQP_STATUS_BAD_AMQP_URL -0x0008 */ + "could not parse AMQP URL", + /* AMQP_STATUS_SOCKET_ERROR -0x0009 */ + "a socket error occurred", + /* AMQP_STATUS_INVALID_PARAMETER -0x000A */ + "invalid parameter", + /* AMQP_STATUS_TABLE_TOO_BIG -0x000B */ + "table too large for buffer", + /* AMQP_STATUS_WRONG_METHOD -0x000C */ + "unexpected method received", + /* AMQP_STATUS_TIMEOUT -0x000D */ + "request timed out", + /* AMQP_STATUS_TIMER_FAILED -0x000E */ + "system timer has failed", + /* AMQP_STATUS_HEARTBEAT_TIMEOUT -0x000F */ + "heartbeat timeout, connection closed", + /* AMQP_STATUS_UNEXPECTED STATE -0x0010 */ + "unexpected protocol state", + /* AMQP_STATUS_SOCKET_CLOSED -0x0011 */ + "socket is closed", + /* AMQP_STATUS_SOCKET_INUSE -0x0012 */ + "socket already open", + /* AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD -0x00013 */ + "unsupported sasl method requested", + /* AMQP_STATUS_UNSUPPORTED -0x0014 */ + "parameter value is unsupported"}; + +static const char *tcp_error_strings[] = { + /* AMQP_STATUS_TCP_ERROR -0x0100 */ + "a socket error occurred", + /* AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR -0x0101 */ + "socket library initialization failed"}; + +static const char *ssl_error_strings[] = { + /* AMQP_STATUS_SSL_ERRO R -0x0200 */ + "a SSL error occurred", + /* AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED -0x0201 */ + "SSL hostname verification failed", + /* AMQP_STATUS_SSL_PEER_VERIFY_FAILED -0x0202 */ + "SSL peer cert verification failed", + /* AMQP_STATUS_SSL_CONNECTION_FAILED -0x0203 */ + "SSL handshake failed"}; + +static const char *unknown_error_string = "(unknown error)"; + +const char *amqp_error_string2(int code) { + const char *error_string; + size_t category = (((-code) & ERROR_CATEGORY_MASK) >> 8); + size_t error = (-code) & ERROR_MASK; + + switch (category) { + case EC_base: + if (error < (sizeof(base_error_strings) / sizeof(char *))) { + error_string = base_error_strings[error]; + } else { + error_string = unknown_error_string; + } + break; + + case EC_tcp: + if (error < (sizeof(tcp_error_strings) / sizeof(char *))) { + error_string = tcp_error_strings[error]; + } else { + error_string = unknown_error_string; + } + break; + + case EC_ssl: + if (error < (sizeof(ssl_error_strings) / sizeof(char *))) { + error_string = ssl_error_strings[error]; + } else { + error_string = unknown_error_string; + } + + break; + + default: + error_string = unknown_error_string; + break; + } + + return error_string; +} + +char *amqp_error_string(int code) { + /* Previously sometimes clients had to flip the sign on a return value from a + * function to get the correct error code. Now, all error codes are negative. + * To keep people's legacy code running correctly, we map all error codes to + * negative values. + * + * This is only done with this deprecated function. + */ + if (code > 0) { + code = -code; + } + return strdup(amqp_error_string2(code)); +} + +void amqp_abort(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + abort(); +} + +const amqp_bytes_t amqp_empty_bytes = {0, NULL}; +const amqp_table_t amqp_empty_table = {0, NULL}; +const amqp_array_t amqp_empty_array = {0, NULL}; + +int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_bytes_t routing_key, + amqp_boolean_t mandatory, amqp_boolean_t immediate, + amqp_basic_properties_t const *properties, + amqp_bytes_t body) { + amqp_frame_t f; + size_t body_offset; + size_t usable_body_payload_size = + state->frame_max - (HEADER_SIZE + FOOTER_SIZE); + int res; + int flagz; + + amqp_basic_publish_t m; + amqp_basic_properties_t default_properties; + + m.exchange = exchange; + m.routing_key = routing_key; + m.mandatory = mandatory; + m.immediate = immediate; + m.ticket = 0; + + /* TODO(alanxz): this heartbeat check is happening in the wrong place, it + * should really be done in amqp_try_send/writev */ + res = amqp_time_has_past(state->next_recv_heartbeat); + if (AMQP_STATUS_TIMER_FAILURE == res) { + return res; + } else if (AMQP_STATUS_TIMEOUT == res) { + res = amqp_try_recv(state); + if (AMQP_STATUS_TIMEOUT == res) { + return AMQP_STATUS_HEARTBEAT_TIMEOUT; + } else if (AMQP_STATUS_OK != res) { + return res; + } + } + + res = amqp_send_method_inner(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m, + AMQP_SF_MORE, amqp_time_infinite()); + if (res < 0) { + return res; + } + + if (properties == NULL) { + memset(&default_properties, 0, sizeof(default_properties)); + properties = &default_properties; + } + + f.frame_type = AMQP_FRAME_HEADER; + f.channel = channel; + f.payload.properties.class_id = AMQP_BASIC_CLASS; + f.payload.properties.body_size = body.len; + f.payload.properties.decoded = (void *)properties; + + if (body.len > 0) { + flagz = AMQP_SF_MORE; + } else { + flagz = AMQP_SF_NONE; + } + res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite()); + if (res < 0) { + return res; + } + + body_offset = 0; + while (body_offset < body.len) { + size_t remaining = body.len - body_offset; + + if (remaining == 0) { + break; + } + + f.frame_type = AMQP_FRAME_BODY; + f.channel = channel; + f.payload.body_fragment.bytes = amqp_offset(body.bytes, body_offset); + if (remaining >= usable_body_payload_size) { + f.payload.body_fragment.len = usable_body_payload_size; + flagz = AMQP_SF_MORE; + } else { + f.payload.body_fragment.len = remaining; + flagz = AMQP_SF_NONE; + } + + body_offset += f.payload.body_fragment.len; + res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite()); + if (res < 0) { + return res; + } + } + + return AMQP_STATUS_OK; +} + +amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state, + amqp_channel_t channel, int code) { + char codestr[13]; + amqp_method_number_t replies[2] = {AMQP_CHANNEL_CLOSE_OK_METHOD, 0}; + amqp_channel_close_t req; + + if (code < 0 || code > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + + req.reply_code = (uint16_t)code; + req.reply_text.bytes = codestr; + req.reply_text.len = sprintf(codestr, "%d", code); + req.class_id = 0; + req.method_id = 0; + + return amqp_simple_rpc(state, channel, AMQP_CHANNEL_CLOSE_METHOD, replies, + &req); +} + +amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state, + int code) { + char codestr[13]; + amqp_method_number_t replies[2] = {AMQP_CONNECTION_CLOSE_OK_METHOD, 0}; + amqp_channel_close_t req; + + if (code < 0 || code > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + + req.reply_code = (uint16_t)code; + req.reply_text.bytes = codestr; + req.reply_text.len = sprintf(codestr, "%d", code); + req.class_id = 0; + req.method_id = 0; + + return amqp_simple_rpc(state, 0, AMQP_CONNECTION_CLOSE_METHOD, replies, &req); +} + +int amqp_basic_ack(amqp_connection_state_t state, amqp_channel_t channel, + uint64_t delivery_tag, amqp_boolean_t multiple) { + amqp_basic_ack_t m; + m.delivery_tag = delivery_tag; + m.multiple = multiple; + return amqp_send_method(state, channel, AMQP_BASIC_ACK_METHOD, &m); +} + +amqp_rpc_reply_t amqp_basic_get(amqp_connection_state_t state, + amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t no_ack) { + amqp_method_number_t replies[] = {AMQP_BASIC_GET_OK_METHOD, + AMQP_BASIC_GET_EMPTY_METHOD, 0}; + amqp_basic_get_t req; + req.ticket = 0; + req.queue = queue; + req.no_ack = no_ack; + + state->most_recent_api_result = + amqp_simple_rpc(state, channel, AMQP_BASIC_GET_METHOD, replies, &req); + return state->most_recent_api_result; +} + +int amqp_basic_reject(amqp_connection_state_t state, amqp_channel_t channel, + uint64_t delivery_tag, amqp_boolean_t requeue) { + amqp_basic_reject_t req; + req.delivery_tag = delivery_tag; + req.requeue = requeue; + return amqp_send_method(state, channel, AMQP_BASIC_REJECT_METHOD, &req); +} + +int amqp_basic_nack(amqp_connection_state_t state, amqp_channel_t channel, + uint64_t delivery_tag, amqp_boolean_t multiple, + amqp_boolean_t requeue) { + amqp_basic_nack_t req; + req.delivery_tag = delivery_tag; + req.multiple = multiple; + req.requeue = requeue; + return amqp_send_method(state, channel, AMQP_BASIC_NACK_METHOD, &req); +} + +struct timeval *amqp_get_handshake_timeout(amqp_connection_state_t state) { + return state->handshake_timeout; +} + +int amqp_set_handshake_timeout(amqp_connection_state_t state, + struct timeval *timeout) { + if (timeout) { + if (timeout->tv_sec < 0 || timeout->tv_usec < 0) { + return AMQP_STATUS_INVALID_PARAMETER; + } + state->internal_handshake_timeout = *timeout; + state->handshake_timeout = &state->internal_handshake_timeout; + } else { + state->handshake_timeout = NULL; + } + + return AMQP_STATUS_OK; +} + +struct timeval *amqp_get_rpc_timeout(amqp_connection_state_t state) { + return state->rpc_timeout; +} + +int amqp_set_rpc_timeout(amqp_connection_state_t state, + struct timeval *timeout) { + if (timeout) { + if (timeout->tv_sec < 0 || timeout->tv_usec < 0) { + return AMQP_STATUS_INVALID_PARAMETER; + } + state->rpc_timeout = &state->internal_rpc_timeout; + *state->rpc_timeout = *timeout; + } else { + state->rpc_timeout = NULL; + } + return AMQP_STATUS_OK; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_connection.c b/ext/librabbitmq/librabbitmq/amqp_connection.c new file mode 100644 index 000000000..034b2e965 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_connection.c @@ -0,0 +1,595 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2014 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "amqp_private.h" +#include "amqp_tcp_socket.h" +#include "amqp_time.h" +#include +#include +#include +#include +#include + +#ifndef AMQP_INITIAL_FRAME_POOL_PAGE_SIZE +#define AMQP_INITIAL_FRAME_POOL_PAGE_SIZE 65536 +#endif + +#ifndef AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE +#define AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE 131072 +#endif + +#ifndef AMQP_DEFAULT_LOGIN_TIMEOUT_SEC +#define AMQP_DEFAULT_LOGIN_TIMEOUT_SEC 12 +#endif + +#define ENFORCE_STATE(statevec, statenum) \ + { \ + amqp_connection_state_t _check_state = (statevec); \ + amqp_connection_state_enum _wanted_state = (statenum); \ + if (_check_state->state != _wanted_state) \ + amqp_abort( \ + "Programming error: invalid AMQP connection state: expected %d, " \ + "got %d", \ + _wanted_state, _check_state->state); \ + } + +amqp_connection_state_t amqp_new_connection(void) { + int res; + amqp_connection_state_t state = (amqp_connection_state_t)calloc( + 1, sizeof(struct amqp_connection_state_t_)); + + if (state == NULL) { + return NULL; + } + + res = amqp_tune_connection(state, 0, AMQP_INITIAL_FRAME_POOL_PAGE_SIZE, 0); + if (0 != res) { + goto out_nomem; + } + + state->inbound_buffer.bytes = state->header_buffer; + state->inbound_buffer.len = sizeof(state->header_buffer); + + state->state = CONNECTION_STATE_INITIAL; + /* the server protocol version response is 8 bytes, which conveniently + is also the minimum frame size */ + state->target_size = 8; + + state->sock_inbound_buffer.len = AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE; + state->sock_inbound_buffer.bytes = + malloc(AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE); + if (state->sock_inbound_buffer.bytes == NULL) { + goto out_nomem; + } + + init_amqp_pool(&state->properties_pool, 512); + + /* Use address of the internal_handshake_timeout object by default. */ + state->internal_handshake_timeout.tv_sec = AMQP_DEFAULT_LOGIN_TIMEOUT_SEC; + state->internal_handshake_timeout.tv_usec = 0; + state->handshake_timeout = &state->internal_handshake_timeout; + + return state; + +out_nomem: + free(state->sock_inbound_buffer.bytes); + free(state); + return NULL; +} + +int amqp_get_sockfd(amqp_connection_state_t state) { + return state->socket ? amqp_socket_get_sockfd(state->socket) : -1; +} + +void amqp_set_sockfd(amqp_connection_state_t state, int sockfd) { + amqp_socket_t *socket = amqp_tcp_socket_new(state); + if (!socket) { + amqp_abort("%s", strerror(errno)); + } + amqp_tcp_socket_set_sockfd(socket, sockfd); +} + +void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket) { + amqp_socket_delete(state->socket); + state->socket = socket; +} + +amqp_socket_t *amqp_get_socket(amqp_connection_state_t state) { + return state->socket; +} + +int amqp_tune_connection(amqp_connection_state_t state, int channel_max, + int frame_max, int heartbeat) { + void *newbuf; + int res; + + ENFORCE_STATE(state, CONNECTION_STATE_IDLE); + + state->channel_max = channel_max; + state->frame_max = frame_max; + + state->heartbeat = heartbeat; + if (0 > state->heartbeat) { + state->heartbeat = 0; + } + + res = amqp_time_s_from_now(&state->next_send_heartbeat, + amqp_heartbeat_send(state)); + if (AMQP_STATUS_OK != res) { + return res; + } + res = amqp_time_s_from_now(&state->next_recv_heartbeat, + amqp_heartbeat_recv(state)); + if (AMQP_STATUS_OK != res) { + return res; + } + + state->outbound_buffer.len = frame_max; + newbuf = realloc(state->outbound_buffer.bytes, frame_max); + if (newbuf == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + state->outbound_buffer.bytes = newbuf; + + return AMQP_STATUS_OK; +} + +int amqp_get_channel_max(amqp_connection_state_t state) { + return state->channel_max; +} + +int amqp_get_frame_max(amqp_connection_state_t state) { + return state->frame_max; +} + +int amqp_get_heartbeat(amqp_connection_state_t state) { + return state->heartbeat; +} + +int amqp_destroy_connection(amqp_connection_state_t state) { + int status = AMQP_STATUS_OK; + if (state) { + int i; + for (i = 0; i < POOL_TABLE_SIZE; ++i) { + amqp_pool_table_entry_t *entry = state->pool_table[i]; + while (NULL != entry) { + amqp_pool_table_entry_t *todelete = entry; + empty_amqp_pool(&entry->pool); + entry = entry->next; + free(todelete); + } + } + + free(state->outbound_buffer.bytes); + free(state->sock_inbound_buffer.bytes); + amqp_socket_delete(state->socket); + empty_amqp_pool(&state->properties_pool); + free(state); + } + return status; +} + +static void return_to_idle(amqp_connection_state_t state) { + state->inbound_buffer.len = sizeof(state->header_buffer); + state->inbound_buffer.bytes = state->header_buffer; + state->inbound_offset = 0; + state->target_size = HEADER_SIZE; + state->state = CONNECTION_STATE_IDLE; +} + +static size_t consume_data(amqp_connection_state_t state, + amqp_bytes_t *received_data) { + /* how much data is available and will fit? */ + size_t bytes_consumed = state->target_size - state->inbound_offset; + if (received_data->len < bytes_consumed) { + bytes_consumed = received_data->len; + } + + memcpy(amqp_offset(state->inbound_buffer.bytes, state->inbound_offset), + received_data->bytes, bytes_consumed); + state->inbound_offset += bytes_consumed; + received_data->bytes = amqp_offset(received_data->bytes, bytes_consumed); + received_data->len -= bytes_consumed; + + return bytes_consumed; +} + +int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data, + amqp_frame_t *decoded_frame) { + size_t bytes_consumed; + void *raw_frame; + + /* Returning frame_type of zero indicates either insufficient input, + or a complete, ignored frame was read. */ + decoded_frame->frame_type = 0; + + if (received_data.len == 0) { + return AMQP_STATUS_OK; + } + + if (state->state == CONNECTION_STATE_IDLE) { + state->state = CONNECTION_STATE_HEADER; + } + + bytes_consumed = consume_data(state, &received_data); + + /* do we have target_size data yet? if not, return with the + expectation that more will arrive */ + if (state->inbound_offset < state->target_size) { + return (int)bytes_consumed; + } + + raw_frame = state->inbound_buffer.bytes; + + switch (state->state) { + case CONNECTION_STATE_INITIAL: + /* check for a protocol header from the server */ + if (memcmp(raw_frame, "AMQP", 4) == 0) { + decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER; + decoded_frame->channel = 0; + + decoded_frame->payload.protocol_header.transport_high = + amqp_d8(amqp_offset(raw_frame, 4)); + decoded_frame->payload.protocol_header.transport_low = + amqp_d8(amqp_offset(raw_frame, 5)); + decoded_frame->payload.protocol_header.protocol_version_major = + amqp_d8(amqp_offset(raw_frame, 6)); + decoded_frame->payload.protocol_header.protocol_version_minor = + amqp_d8(amqp_offset(raw_frame, 7)); + + return_to_idle(state); + return (int)bytes_consumed; + } + + /* it's not a protocol header; fall through to process it as a + regular frame header */ + + case CONNECTION_STATE_HEADER: { + amqp_channel_t channel; + amqp_pool_t *channel_pool; + /* frame length is 3 bytes in */ + channel = amqp_d16(amqp_offset(raw_frame, 1)); + + state->target_size = + amqp_d32(amqp_offset(raw_frame, 3)) + HEADER_SIZE + FOOTER_SIZE; + + if ((size_t)state->frame_max < state->target_size) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + channel_pool = amqp_get_or_create_channel_pool(state, channel); + if (NULL == channel_pool) { + return AMQP_STATUS_NO_MEMORY; + } + + amqp_pool_alloc_bytes(channel_pool, state->target_size, + &state->inbound_buffer); + if (NULL == state->inbound_buffer.bytes) { + return AMQP_STATUS_NO_MEMORY; + } + memcpy(state->inbound_buffer.bytes, state->header_buffer, HEADER_SIZE); + raw_frame = state->inbound_buffer.bytes; + + state->state = CONNECTION_STATE_BODY; + + bytes_consumed += consume_data(state, &received_data); + + /* do we have target_size data yet? if not, return with the + expectation that more will arrive */ + if (state->inbound_offset < state->target_size) { + return (int)bytes_consumed; + } + } + /* fall through to process body */ + + case CONNECTION_STATE_BODY: { + amqp_bytes_t encoded; + int res; + amqp_pool_t *channel_pool; + + /* Check frame end marker (footer) */ + if (amqp_d8(amqp_offset(raw_frame, state->target_size - 1)) != + AMQP_FRAME_END) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + decoded_frame->frame_type = amqp_d8(amqp_offset(raw_frame, 0)); + decoded_frame->channel = amqp_d16(amqp_offset(raw_frame, 1)); + + channel_pool = + amqp_get_or_create_channel_pool(state, decoded_frame->channel); + if (NULL == channel_pool) { + return AMQP_STATUS_NO_MEMORY; + } + + switch (decoded_frame->frame_type) { + case AMQP_FRAME_METHOD: + decoded_frame->payload.method.id = + amqp_d32(amqp_offset(raw_frame, HEADER_SIZE)); + encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 4); + encoded.len = state->target_size - HEADER_SIZE - 4 - FOOTER_SIZE; + + res = amqp_decode_method(decoded_frame->payload.method.id, + channel_pool, encoded, + &decoded_frame->payload.method.decoded); + if (res < 0) { + return res; + } + + break; + + case AMQP_FRAME_HEADER: + decoded_frame->payload.properties.class_id = + amqp_d16(amqp_offset(raw_frame, HEADER_SIZE)); + /* unused 2-byte weight field goes here */ + decoded_frame->payload.properties.body_size = + amqp_d64(amqp_offset(raw_frame, HEADER_SIZE + 4)); + encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 12); + encoded.len = state->target_size - HEADER_SIZE - 12 - FOOTER_SIZE; + decoded_frame->payload.properties.raw = encoded; + + res = amqp_decode_properties( + decoded_frame->payload.properties.class_id, channel_pool, encoded, + &decoded_frame->payload.properties.decoded); + if (res < 0) { + return res; + } + + break; + + case AMQP_FRAME_BODY: + decoded_frame->payload.body_fragment.len = + state->target_size - HEADER_SIZE - FOOTER_SIZE; + decoded_frame->payload.body_fragment.bytes = + amqp_offset(raw_frame, HEADER_SIZE); + break; + + case AMQP_FRAME_HEARTBEAT: + break; + + default: + /* Ignore the frame */ + decoded_frame->frame_type = 0; + break; + } + + return_to_idle(state); + return (int)bytes_consumed; + } + + default: + amqp_abort("Internal error: invalid amqp_connection_state_t->state %d", + state->state); + } +} + +amqp_boolean_t amqp_release_buffers_ok(amqp_connection_state_t state) { + return (state->state == CONNECTION_STATE_IDLE); +} + +void amqp_release_buffers(amqp_connection_state_t state) { + int i; + ENFORCE_STATE(state, CONNECTION_STATE_IDLE); + + for (i = 0; i < POOL_TABLE_SIZE; ++i) { + amqp_pool_table_entry_t *entry = state->pool_table[i]; + + for (; NULL != entry; entry = entry->next) { + amqp_maybe_release_buffers_on_channel(state, entry->channel); + } + } +} + +void amqp_maybe_release_buffers(amqp_connection_state_t state) { + if (amqp_release_buffers_ok(state)) { + amqp_release_buffers(state); + } +} + +void amqp_maybe_release_buffers_on_channel(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_link_t *queued_link; + amqp_pool_t *pool; + if (CONNECTION_STATE_IDLE != state->state) { + return; + } + + queued_link = state->first_queued_frame; + + while (NULL != queued_link) { + amqp_frame_t *frame = queued_link->data; + if (channel == frame->channel) { + return; + } + + queued_link = queued_link->next; + } + + pool = amqp_get_channel_pool(state, channel); + + if (pool != NULL) { + recycle_amqp_pool(pool); + } +} + +static int amqp_frame_to_bytes(const amqp_frame_t *frame, amqp_bytes_t buffer, + amqp_bytes_t *encoded) { + void *out_frame = buffer.bytes; + size_t out_frame_len; + int res; + + amqp_e8(frame->frame_type, amqp_offset(out_frame, 0)); + amqp_e16(frame->channel, amqp_offset(out_frame, 1)); + + switch (frame->frame_type) { + case AMQP_FRAME_BODY: { + const amqp_bytes_t *body = &frame->payload.body_fragment; + + memcpy(amqp_offset(out_frame, HEADER_SIZE), body->bytes, body->len); + + out_frame_len = body->len; + break; + } + case AMQP_FRAME_METHOD: { + amqp_bytes_t method_encoded; + + amqp_e32(frame->payload.method.id, amqp_offset(out_frame, HEADER_SIZE)); + + method_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 4); + method_encoded.len = buffer.len - HEADER_SIZE - 4 - FOOTER_SIZE; + + res = amqp_encode_method(frame->payload.method.id, + frame->payload.method.decoded, method_encoded); + if (res < 0) { + return res; + } + + out_frame_len = res + 4; + break; + } + + case AMQP_FRAME_HEADER: { + amqp_bytes_t properties_encoded; + + amqp_e16(frame->payload.properties.class_id, + amqp_offset(out_frame, HEADER_SIZE)); + amqp_e16(0, amqp_offset(out_frame, HEADER_SIZE + 2)); /* "weight" */ + amqp_e64(frame->payload.properties.body_size, + amqp_offset(out_frame, HEADER_SIZE + 4)); + + properties_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 12); + properties_encoded.len = buffer.len - HEADER_SIZE - 12 - FOOTER_SIZE; + + res = amqp_encode_properties(frame->payload.properties.class_id, + frame->payload.properties.decoded, + properties_encoded); + if (res < 0) { + return res; + } + + out_frame_len = res + 12; + break; + } + + case AMQP_FRAME_HEARTBEAT: + out_frame_len = 0; + break; + + default: + return AMQP_STATUS_INVALID_PARAMETER; + } + + amqp_e32((uint32_t)out_frame_len, amqp_offset(out_frame, 3)); + amqp_e8(AMQP_FRAME_END, amqp_offset(out_frame, HEADER_SIZE + out_frame_len)); + + encoded->bytes = out_frame; + encoded->len = out_frame_len + HEADER_SIZE + FOOTER_SIZE; + + return AMQP_STATUS_OK; +} + +int amqp_send_frame(amqp_connection_state_t state, const amqp_frame_t *frame) { + return amqp_send_frame_inner(state, frame, AMQP_SF_NONE, + amqp_time_infinite()); +} + +int amqp_send_frame_inner(amqp_connection_state_t state, + const amqp_frame_t *frame, int flags, + amqp_time_t deadline) { + int res; + ssize_t sent; + amqp_bytes_t encoded; + amqp_time_t next_timeout; + + /* TODO: if the AMQP_SF_MORE socket optimization can be shown to work + * correctly, then this could be un-done so that body-frames are sent as 3 + * send calls, getting rid of the copy of the body content, some testing + * would need to be done to see if this would actually a win for performance. + * */ + res = amqp_frame_to_bytes(frame, state->outbound_buffer, &encoded); + if (AMQP_STATUS_OK != res) { + return res; + } + +start_send: + + next_timeout = amqp_time_first(deadline, state->next_recv_heartbeat); + + sent = amqp_try_send(state, encoded.bytes, encoded.len, next_timeout, flags); + if (0 > sent) { + return (int)sent; + } + + /* A partial send has occurred, because of a heartbeat timeout (so try recv + * something) or common timeout (so return AMQP_STATUS_TIMEOUT) */ + if ((ssize_t)encoded.len != sent) { + if (amqp_time_equal(next_timeout, deadline)) { + /* timeout of method was received, so return from method*/ + return AMQP_STATUS_TIMEOUT; + } + + res = amqp_try_recv(state); + + if (AMQP_STATUS_TIMEOUT == res) { + return AMQP_STATUS_HEARTBEAT_TIMEOUT; + } else if (AMQP_STATUS_OK != res) { + return res; + } + + encoded.bytes = (uint8_t *)encoded.bytes + sent; + encoded.len -= sent; + goto start_send; + } + + res = amqp_time_s_from_now(&state->next_send_heartbeat, + amqp_heartbeat_send(state)); + return res; +} + +amqp_table_t *amqp_get_server_properties(amqp_connection_state_t state) { + return &state->server_properties; +} + +amqp_table_t *amqp_get_client_properties(amqp_connection_state_t state) { + return &state->client_properties; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_consumer.c b/ext/librabbitmq/librabbitmq/amqp_consumer.c new file mode 100644 index 000000000..bb9095f92 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_consumer.c @@ -0,0 +1,307 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 + * Alan Antonuk. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ +#include "amqp.h" +#include "amqp_private.h" +#include "amqp_socket.h" + +#include +#include + +static int amqp_basic_properties_clone(amqp_basic_properties_t *original, + amqp_basic_properties_t *clone, + amqp_pool_t *pool) { + memset(clone, 0, sizeof(*clone)); + clone->_flags = original->_flags; + +#define CLONE_BYTES_POOL(original, clone, pool) \ + if (0 == original.len) { \ + clone = amqp_empty_bytes; \ + } else { \ + amqp_pool_alloc_bytes(pool, original.len, &clone); \ + if (NULL == clone.bytes) { \ + return AMQP_STATUS_NO_MEMORY; \ + } \ + memcpy(clone.bytes, original.bytes, clone.len); \ + } + + if (clone->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + CLONE_BYTES_POOL(original->content_type, clone->content_type, pool) + } + + if (clone->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { + CLONE_BYTES_POOL(original->content_encoding, clone->content_encoding, pool) + } + + if (clone->_flags & AMQP_BASIC_HEADERS_FLAG) { + int res = amqp_table_clone(&original->headers, &clone->headers, pool); + if (AMQP_STATUS_OK != res) { + return res; + } + } + + if (clone->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { + clone->delivery_mode = original->delivery_mode; + } + + if (clone->_flags & AMQP_BASIC_PRIORITY_FLAG) { + clone->priority = original->priority; + } + + if (clone->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) { + CLONE_BYTES_POOL(original->correlation_id, clone->correlation_id, pool) + } + + if (clone->_flags & AMQP_BASIC_REPLY_TO_FLAG) { + CLONE_BYTES_POOL(original->reply_to, clone->reply_to, pool) + } + + if (clone->_flags & AMQP_BASIC_EXPIRATION_FLAG) { + CLONE_BYTES_POOL(original->expiration, clone->expiration, pool) + } + + if (clone->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) { + CLONE_BYTES_POOL(original->message_id, clone->message_id, pool) + } + + if (clone->_flags & AMQP_BASIC_TIMESTAMP_FLAG) { + clone->timestamp = original->timestamp; + } + + if (clone->_flags & AMQP_BASIC_TYPE_FLAG) { + CLONE_BYTES_POOL(original->type, clone->type, pool) + } + + if (clone->_flags & AMQP_BASIC_USER_ID_FLAG) { + CLONE_BYTES_POOL(original->user_id, clone->user_id, pool) + } + + if (clone->_flags & AMQP_BASIC_APP_ID_FLAG) { + CLONE_BYTES_POOL(original->app_id, clone->app_id, pool) + } + + if (clone->_flags & AMQP_BASIC_CLUSTER_ID_FLAG) { + CLONE_BYTES_POOL(original->cluster_id, clone->cluster_id, pool) + } + + return AMQP_STATUS_OK; +#undef CLONE_BYTES_POOL +} + +void amqp_destroy_message(amqp_message_t *message) { + empty_amqp_pool(&message->pool); + amqp_bytes_free(message->body); +} + +void amqp_destroy_envelope(amqp_envelope_t *envelope) { + amqp_destroy_message(&envelope->message); + amqp_bytes_free(envelope->routing_key); + amqp_bytes_free(envelope->exchange); + amqp_bytes_free(envelope->consumer_tag); +} + +static int amqp_bytes_malloc_dup_failed(amqp_bytes_t bytes) { + if (bytes.len != 0 && bytes.bytes == NULL) { + return 1; + } + return 0; +} + +amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state, + amqp_envelope_t *envelope, + struct timeval *timeout, + AMQP_UNUSED int flags) { + int res; + amqp_frame_t frame; + amqp_basic_deliver_t *delivery_method; + amqp_rpc_reply_t ret; + + memset(&ret, 0, sizeof(ret)); + memset(envelope, 0, sizeof(*envelope)); + + res = amqp_simple_wait_frame_noblock(state, &frame, timeout); + if (AMQP_STATUS_OK != res) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = res; + goto error_out1; + } + + if (AMQP_FRAME_METHOD != frame.frame_type || + AMQP_BASIC_DELIVER_METHOD != frame.payload.method.id) { + amqp_put_back_frame(state, &frame); + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_UNEXPECTED_STATE; + goto error_out1; + } + + delivery_method = frame.payload.method.decoded; + + envelope->channel = frame.channel; + envelope->consumer_tag = amqp_bytes_malloc_dup(delivery_method->consumer_tag); + envelope->delivery_tag = delivery_method->delivery_tag; + envelope->redelivered = delivery_method->redelivered; + envelope->exchange = amqp_bytes_malloc_dup(delivery_method->exchange); + envelope->routing_key = amqp_bytes_malloc_dup(delivery_method->routing_key); + + if (amqp_bytes_malloc_dup_failed(envelope->consumer_tag) || + amqp_bytes_malloc_dup_failed(envelope->exchange) || + amqp_bytes_malloc_dup_failed(envelope->routing_key)) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_NO_MEMORY; + goto error_out2; + } + + ret = amqp_read_message(state, envelope->channel, &envelope->message, 0); + if (AMQP_RESPONSE_NORMAL != ret.reply_type) { + goto error_out2; + } + + ret.reply_type = AMQP_RESPONSE_NORMAL; + return ret; + +error_out2: + amqp_bytes_free(envelope->routing_key); + amqp_bytes_free(envelope->exchange); + amqp_bytes_free(envelope->consumer_tag); +error_out1: + return ret; +} + +amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_message_t *message, + AMQP_UNUSED int flags) { + amqp_frame_t frame; + amqp_rpc_reply_t ret; + + size_t body_read; + char *body_read_ptr; + int res; + + memset(&ret, 0, sizeof(ret)); + memset(message, 0, sizeof(*message)); + + res = amqp_simple_wait_frame_on_channel(state, channel, &frame); + if (AMQP_STATUS_OK != res) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = res; + + goto error_out1; + } + + if (AMQP_FRAME_HEADER != frame.frame_type) { + if (AMQP_FRAME_METHOD == frame.frame_type && + (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id || + AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) { + + ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; + ret.reply = frame.payload.method; + + } else { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_UNEXPECTED_STATE; + + amqp_put_back_frame(state, &frame); + } + goto error_out1; + } + + init_amqp_pool(&message->pool, 4096); + res = amqp_basic_properties_clone(frame.payload.properties.decoded, + &message->properties, &message->pool); + + if (AMQP_STATUS_OK != res) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = res; + goto error_out3; + } + + if (0 == frame.payload.properties.body_size) { + message->body = amqp_empty_bytes; + } else { + if (SIZE_MAX < frame.payload.properties.body_size) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_NO_MEMORY; + goto error_out1; + } + message->body = + amqp_bytes_malloc((size_t)frame.payload.properties.body_size); + if (NULL == message->body.bytes) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_NO_MEMORY; + goto error_out1; + } + } + + body_read = 0; + body_read_ptr = message->body.bytes; + + while (body_read < message->body.len) { + res = amqp_simple_wait_frame_on_channel(state, channel, &frame); + if (AMQP_STATUS_OK != res) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = res; + goto error_out2; + } + if (AMQP_FRAME_BODY != frame.frame_type) { + if (AMQP_FRAME_METHOD == frame.frame_type && + (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id || + AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) { + + ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; + ret.reply = frame.payload.method; + } else { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_BAD_AMQP_DATA; + } + goto error_out2; + } + + if (body_read + frame.payload.body_fragment.len > message->body.len) { + ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + ret.library_error = AMQP_STATUS_BAD_AMQP_DATA; + goto error_out2; + } + + memcpy(body_read_ptr, frame.payload.body_fragment.bytes, + frame.payload.body_fragment.len); + + body_read += frame.payload.body_fragment.len; + body_read_ptr += frame.payload.body_fragment.len; + } + + ret.reply_type = AMQP_RESPONSE_NORMAL; + return ret; + +error_out2: + amqp_bytes_free(message->body); +error_out3: + empty_amqp_pool(&message->pool); +error_out1: + return ret; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_framing.c b/ext/librabbitmq/librabbitmq/amqp_framing.c new file mode 100644 index 000000000..bdeb01cd4 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_framing.c @@ -0,0 +1,2876 @@ +/* Generated code. Do not edit. Edit and re-run codegen.py instead. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_private.h" +#include +#include +#include +#include + +char const *amqp_constant_name(int constantNumber) { + switch (constantNumber) { + case AMQP_FRAME_METHOD: + return "AMQP_FRAME_METHOD"; + case AMQP_FRAME_HEADER: + return "AMQP_FRAME_HEADER"; + case AMQP_FRAME_BODY: + return "AMQP_FRAME_BODY"; + case AMQP_FRAME_HEARTBEAT: + return "AMQP_FRAME_HEARTBEAT"; + case AMQP_FRAME_MIN_SIZE: + return "AMQP_FRAME_MIN_SIZE"; + case AMQP_FRAME_END: + return "AMQP_FRAME_END"; + case AMQP_REPLY_SUCCESS: + return "AMQP_REPLY_SUCCESS"; + case AMQP_CONTENT_TOO_LARGE: + return "AMQP_CONTENT_TOO_LARGE"; + case AMQP_NO_ROUTE: + return "AMQP_NO_ROUTE"; + case AMQP_NO_CONSUMERS: + return "AMQP_NO_CONSUMERS"; + case AMQP_ACCESS_REFUSED: + return "AMQP_ACCESS_REFUSED"; + case AMQP_NOT_FOUND: + return "AMQP_NOT_FOUND"; + case AMQP_RESOURCE_LOCKED: + return "AMQP_RESOURCE_LOCKED"; + case AMQP_PRECONDITION_FAILED: + return "AMQP_PRECONDITION_FAILED"; + case AMQP_CONNECTION_FORCED: + return "AMQP_CONNECTION_FORCED"; + case AMQP_INVALID_PATH: + return "AMQP_INVALID_PATH"; + case AMQP_FRAME_ERROR: + return "AMQP_FRAME_ERROR"; + case AMQP_SYNTAX_ERROR: + return "AMQP_SYNTAX_ERROR"; + case AMQP_COMMAND_INVALID: + return "AMQP_COMMAND_INVALID"; + case AMQP_CHANNEL_ERROR: + return "AMQP_CHANNEL_ERROR"; + case AMQP_UNEXPECTED_FRAME: + return "AMQP_UNEXPECTED_FRAME"; + case AMQP_RESOURCE_ERROR: + return "AMQP_RESOURCE_ERROR"; + case AMQP_NOT_ALLOWED: + return "AMQP_NOT_ALLOWED"; + case AMQP_NOT_IMPLEMENTED: + return "AMQP_NOT_IMPLEMENTED"; + case AMQP_INTERNAL_ERROR: + return "AMQP_INTERNAL_ERROR"; + default: + return "(unknown)"; + } +} + +amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) { + switch (constantNumber) { + case AMQP_CONNECTION_FORCED: + return 1; + case AMQP_INVALID_PATH: + return 1; + case AMQP_FRAME_ERROR: + return 1; + case AMQP_SYNTAX_ERROR: + return 1; + case AMQP_COMMAND_INVALID: + return 1; + case AMQP_CHANNEL_ERROR: + return 1; + case AMQP_UNEXPECTED_FRAME: + return 1; + case AMQP_RESOURCE_ERROR: + return 1; + case AMQP_NOT_ALLOWED: + return 1; + case AMQP_NOT_IMPLEMENTED: + return 1; + case AMQP_INTERNAL_ERROR: + return 1; + default: + return 0; + } +} + +char const *amqp_method_name(amqp_method_number_t methodNumber) { + switch (methodNumber) { + case AMQP_CONNECTION_START_METHOD: + return "AMQP_CONNECTION_START_METHOD"; + case AMQP_CONNECTION_START_OK_METHOD: + return "AMQP_CONNECTION_START_OK_METHOD"; + case AMQP_CONNECTION_SECURE_METHOD: + return "AMQP_CONNECTION_SECURE_METHOD"; + case AMQP_CONNECTION_SECURE_OK_METHOD: + return "AMQP_CONNECTION_SECURE_OK_METHOD"; + case AMQP_CONNECTION_TUNE_METHOD: + return "AMQP_CONNECTION_TUNE_METHOD"; + case AMQP_CONNECTION_TUNE_OK_METHOD: + return "AMQP_CONNECTION_TUNE_OK_METHOD"; + case AMQP_CONNECTION_OPEN_METHOD: + return "AMQP_CONNECTION_OPEN_METHOD"; + case AMQP_CONNECTION_OPEN_OK_METHOD: + return "AMQP_CONNECTION_OPEN_OK_METHOD"; + case AMQP_CONNECTION_CLOSE_METHOD: + return "AMQP_CONNECTION_CLOSE_METHOD"; + case AMQP_CONNECTION_CLOSE_OK_METHOD: + return "AMQP_CONNECTION_CLOSE_OK_METHOD"; + case AMQP_CONNECTION_BLOCKED_METHOD: + return "AMQP_CONNECTION_BLOCKED_METHOD"; + case AMQP_CONNECTION_UNBLOCKED_METHOD: + return "AMQP_CONNECTION_UNBLOCKED_METHOD"; + case AMQP_CHANNEL_OPEN_METHOD: + return "AMQP_CHANNEL_OPEN_METHOD"; + case AMQP_CHANNEL_OPEN_OK_METHOD: + return "AMQP_CHANNEL_OPEN_OK_METHOD"; + case AMQP_CHANNEL_FLOW_METHOD: + return "AMQP_CHANNEL_FLOW_METHOD"; + case AMQP_CHANNEL_FLOW_OK_METHOD: + return "AMQP_CHANNEL_FLOW_OK_METHOD"; + case AMQP_CHANNEL_CLOSE_METHOD: + return "AMQP_CHANNEL_CLOSE_METHOD"; + case AMQP_CHANNEL_CLOSE_OK_METHOD: + return "AMQP_CHANNEL_CLOSE_OK_METHOD"; + case AMQP_ACCESS_REQUEST_METHOD: + return "AMQP_ACCESS_REQUEST_METHOD"; + case AMQP_ACCESS_REQUEST_OK_METHOD: + return "AMQP_ACCESS_REQUEST_OK_METHOD"; + case AMQP_EXCHANGE_DECLARE_METHOD: + return "AMQP_EXCHANGE_DECLARE_METHOD"; + case AMQP_EXCHANGE_DECLARE_OK_METHOD: + return "AMQP_EXCHANGE_DECLARE_OK_METHOD"; + case AMQP_EXCHANGE_DELETE_METHOD: + return "AMQP_EXCHANGE_DELETE_METHOD"; + case AMQP_EXCHANGE_DELETE_OK_METHOD: + return "AMQP_EXCHANGE_DELETE_OK_METHOD"; + case AMQP_EXCHANGE_BIND_METHOD: + return "AMQP_EXCHANGE_BIND_METHOD"; + case AMQP_EXCHANGE_BIND_OK_METHOD: + return "AMQP_EXCHANGE_BIND_OK_METHOD"; + case AMQP_EXCHANGE_UNBIND_METHOD: + return "AMQP_EXCHANGE_UNBIND_METHOD"; + case AMQP_EXCHANGE_UNBIND_OK_METHOD: + return "AMQP_EXCHANGE_UNBIND_OK_METHOD"; + case AMQP_QUEUE_DECLARE_METHOD: + return "AMQP_QUEUE_DECLARE_METHOD"; + case AMQP_QUEUE_DECLARE_OK_METHOD: + return "AMQP_QUEUE_DECLARE_OK_METHOD"; + case AMQP_QUEUE_BIND_METHOD: + return "AMQP_QUEUE_BIND_METHOD"; + case AMQP_QUEUE_BIND_OK_METHOD: + return "AMQP_QUEUE_BIND_OK_METHOD"; + case AMQP_QUEUE_PURGE_METHOD: + return "AMQP_QUEUE_PURGE_METHOD"; + case AMQP_QUEUE_PURGE_OK_METHOD: + return "AMQP_QUEUE_PURGE_OK_METHOD"; + case AMQP_QUEUE_DELETE_METHOD: + return "AMQP_QUEUE_DELETE_METHOD"; + case AMQP_QUEUE_DELETE_OK_METHOD: + return "AMQP_QUEUE_DELETE_OK_METHOD"; + case AMQP_QUEUE_UNBIND_METHOD: + return "AMQP_QUEUE_UNBIND_METHOD"; + case AMQP_QUEUE_UNBIND_OK_METHOD: + return "AMQP_QUEUE_UNBIND_OK_METHOD"; + case AMQP_BASIC_QOS_METHOD: + return "AMQP_BASIC_QOS_METHOD"; + case AMQP_BASIC_QOS_OK_METHOD: + return "AMQP_BASIC_QOS_OK_METHOD"; + case AMQP_BASIC_CONSUME_METHOD: + return "AMQP_BASIC_CONSUME_METHOD"; + case AMQP_BASIC_CONSUME_OK_METHOD: + return "AMQP_BASIC_CONSUME_OK_METHOD"; + case AMQP_BASIC_CANCEL_METHOD: + return "AMQP_BASIC_CANCEL_METHOD"; + case AMQP_BASIC_CANCEL_OK_METHOD: + return "AMQP_BASIC_CANCEL_OK_METHOD"; + case AMQP_BASIC_PUBLISH_METHOD: + return "AMQP_BASIC_PUBLISH_METHOD"; + case AMQP_BASIC_RETURN_METHOD: + return "AMQP_BASIC_RETURN_METHOD"; + case AMQP_BASIC_DELIVER_METHOD: + return "AMQP_BASIC_DELIVER_METHOD"; + case AMQP_BASIC_GET_METHOD: + return "AMQP_BASIC_GET_METHOD"; + case AMQP_BASIC_GET_OK_METHOD: + return "AMQP_BASIC_GET_OK_METHOD"; + case AMQP_BASIC_GET_EMPTY_METHOD: + return "AMQP_BASIC_GET_EMPTY_METHOD"; + case AMQP_BASIC_ACK_METHOD: + return "AMQP_BASIC_ACK_METHOD"; + case AMQP_BASIC_REJECT_METHOD: + return "AMQP_BASIC_REJECT_METHOD"; + case AMQP_BASIC_RECOVER_ASYNC_METHOD: + return "AMQP_BASIC_RECOVER_ASYNC_METHOD"; + case AMQP_BASIC_RECOVER_METHOD: + return "AMQP_BASIC_RECOVER_METHOD"; + case AMQP_BASIC_RECOVER_OK_METHOD: + return "AMQP_BASIC_RECOVER_OK_METHOD"; + case AMQP_BASIC_NACK_METHOD: + return "AMQP_BASIC_NACK_METHOD"; + case AMQP_TX_SELECT_METHOD: + return "AMQP_TX_SELECT_METHOD"; + case AMQP_TX_SELECT_OK_METHOD: + return "AMQP_TX_SELECT_OK_METHOD"; + case AMQP_TX_COMMIT_METHOD: + return "AMQP_TX_COMMIT_METHOD"; + case AMQP_TX_COMMIT_OK_METHOD: + return "AMQP_TX_COMMIT_OK_METHOD"; + case AMQP_TX_ROLLBACK_METHOD: + return "AMQP_TX_ROLLBACK_METHOD"; + case AMQP_TX_ROLLBACK_OK_METHOD: + return "AMQP_TX_ROLLBACK_OK_METHOD"; + case AMQP_CONFIRM_SELECT_METHOD: + return "AMQP_CONFIRM_SELECT_METHOD"; + case AMQP_CONFIRM_SELECT_OK_METHOD: + return "AMQP_CONFIRM_SELECT_OK_METHOD"; + default: + return NULL; + } +} + +amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) { + switch (methodNumber) { + case AMQP_BASIC_PUBLISH_METHOD: + return 1; + case AMQP_BASIC_RETURN_METHOD: + return 1; + case AMQP_BASIC_DELIVER_METHOD: + return 1; + case AMQP_BASIC_GET_OK_METHOD: + return 1; + default: + return 0; + } +} + +int amqp_decode_method(amqp_method_number_t methodNumber, amqp_pool_t *pool, + amqp_bytes_t encoded, void **decoded) { + size_t offset = 0; + uint8_t bit_buffer; + + switch (methodNumber) { + case AMQP_CONNECTION_START_METHOD: { + amqp_connection_start_t *m = (amqp_connection_start_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_start_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &m->version_major)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &m->version_minor)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = + amqp_decode_table(encoded, pool, &(m->server_properties), &offset); + if (res < 0) return res; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->mechanisms, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->locales, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_START_OK_METHOD: { + amqp_connection_start_ok_t *m = + (amqp_connection_start_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_start_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + int res = + amqp_decode_table(encoded, pool, &(m->client_properties), &offset); + if (res < 0) return res; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->mechanism, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->response, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->locale, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_SECURE_METHOD: { + amqp_connection_secure_t *m = (amqp_connection_secure_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_secure_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->challenge, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_SECURE_OK_METHOD: { + amqp_connection_secure_ok_t *m = + (amqp_connection_secure_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_secure_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->response, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_TUNE_METHOD: { + amqp_connection_tune_t *m = (amqp_connection_tune_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_tune_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_CONNECTION_TUNE_OK_METHOD: { + amqp_connection_tune_ok_t *m = + (amqp_connection_tune_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_tune_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_CONNECTION_OPEN_METHOD: { + amqp_connection_open_t *m = (amqp_connection_open_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_open_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->virtual_host, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->capabilities, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->insist = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_CONNECTION_OPEN_OK_METHOD: { + amqp_connection_open_ok_t *m = + (amqp_connection_open_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_open_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->known_hosts, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_CLOSE_METHOD: { + amqp_connection_close_t *m = (amqp_connection_close_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_close_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_16(encoded, &offset, &m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_CONNECTION_CLOSE_OK_METHOD: { + amqp_connection_close_ok_t *m = + (amqp_connection_close_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_close_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_BLOCKED_METHOD: { + amqp_connection_blocked_t *m = + (amqp_connection_blocked_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_blocked_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reason, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CONNECTION_UNBLOCKED_METHOD: { + amqp_connection_unblocked_t *m = + (amqp_connection_unblocked_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_unblocked_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_CHANNEL_OPEN_METHOD: { + amqp_channel_open_t *m = (amqp_channel_open_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_open_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->out_of_band, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CHANNEL_OPEN_OK_METHOD: { + amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_open_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint32_t len; + if (!amqp_decode_32(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->channel_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_CHANNEL_FLOW_METHOD: { + amqp_channel_flow_t *m = (amqp_channel_flow_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_flow_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->active = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_CHANNEL_FLOW_OK_METHOD: { + amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_flow_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->active = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_CHANNEL_CLOSE_METHOD: { + amqp_channel_close_t *m = (amqp_channel_close_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_close_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_16(encoded, &offset, &m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_CHANNEL_CLOSE_OK_METHOD: { + amqp_channel_close_ok_t *m = (amqp_channel_close_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_close_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_ACCESS_REQUEST_METHOD: { + amqp_access_request_t *m = (amqp_access_request_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_request_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->realm, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->exclusive = (bit_buffer & (1 << 0)) ? 1 : 0; + m->passive = (bit_buffer & (1 << 1)) ? 1 : 0; + m->active = (bit_buffer & (1 << 2)) ? 1 : 0; + m->write = (bit_buffer & (1 << 3)) ? 1 : 0; + m->read = (bit_buffer & (1 << 4)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_ACCESS_REQUEST_OK_METHOD: { + amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_request_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_DECLARE_METHOD: { + amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_declare_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->passive = (bit_buffer & (1 << 0)) ? 1 : 0; + m->durable = (bit_buffer & (1 << 1)) ? 1 : 0; + m->auto_delete = (bit_buffer & (1 << 2)) ? 1 : 0; + m->internal = (bit_buffer & (1 << 3)) ? 1 : 0; + m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_DECLARE_OK_METHOD: { + amqp_exchange_declare_ok_t *m = + (amqp_exchange_declare_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_declare_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_DELETE_METHOD: { + amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_delete_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0; + m->nowait = (bit_buffer & (1 << 1)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_DELETE_OK_METHOD: { + amqp_exchange_delete_ok_t *m = + (amqp_exchange_delete_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_delete_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_BIND_METHOD: { + amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_bind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->destination, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->source, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_BIND_OK_METHOD: { + amqp_exchange_bind_ok_t *m = (amqp_exchange_bind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_bind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_UNBIND_METHOD: { + amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_unbind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->destination, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->source, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_EXCHANGE_UNBIND_OK_METHOD: { + amqp_exchange_unbind_ok_t *m = + (amqp_exchange_unbind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_unbind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_QUEUE_DECLARE_METHOD: { + amqp_queue_declare_t *m = (amqp_queue_declare_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_declare_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->passive = (bit_buffer & (1 << 0)) ? 1 : 0; + m->durable = (bit_buffer & (1 << 1)) ? 1 : 0; + m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0; + m->auto_delete = (bit_buffer & (1 << 3)) ? 1 : 0; + m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_QUEUE_DECLARE_OK_METHOD: { + amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_declare_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_32(encoded, &offset, &m->consumer_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_QUEUE_BIND_METHOD: { + amqp_queue_bind_t *m = + (amqp_queue_bind_t *)amqp_pool_alloc(pool, sizeof(amqp_queue_bind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_QUEUE_BIND_OK_METHOD: { + amqp_queue_bind_ok_t *m = (amqp_queue_bind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_bind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_QUEUE_PURGE_METHOD: { + amqp_queue_purge_t *m = (amqp_queue_purge_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_purge_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_QUEUE_PURGE_OK_METHOD: { + amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_purge_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_QUEUE_DELETE_METHOD: { + amqp_queue_delete_t *m = (amqp_queue_delete_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_delete_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0; + m->if_empty = (bit_buffer & (1 << 1)) ? 1 : 0; + m->nowait = (bit_buffer & (1 << 2)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_QUEUE_DELETE_OK_METHOD: { + amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_delete_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_QUEUE_UNBIND_METHOD: { + amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_unbind_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_QUEUE_UNBIND_OK_METHOD: { + amqp_queue_unbind_ok_t *m = (amqp_queue_unbind_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_unbind_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_QOS_METHOD: { + amqp_basic_qos_t *m = + (amqp_basic_qos_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_qos_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_32(encoded, &offset, &m->prefetch_size)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_16(encoded, &offset, &m->prefetch_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->global = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_QOS_OK_METHOD: { + amqp_basic_qos_ok_t *m = (amqp_basic_qos_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_qos_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_CONSUME_METHOD: { + amqp_basic_consume_t *m = (amqp_basic_consume_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_consume_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->no_local = (bit_buffer & (1 << 0)) ? 1 : 0; + m->no_ack = (bit_buffer & (1 << 1)) ? 1 : 0; + m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0; + m->nowait = (bit_buffer & (1 << 3)) ? 1 : 0; + { + int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset); + if (res < 0) return res; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_CONSUME_OK_METHOD: { + amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_consume_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_CANCEL_METHOD: { + amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_cancel_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_CANCEL_OK_METHOD: { + amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_cancel_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_PUBLISH_METHOD: { + amqp_basic_publish_t *m = (amqp_basic_publish_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_publish_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->mandatory = (bit_buffer & (1 << 0)) ? 1 : 0; + m->immediate = (bit_buffer & (1 << 1)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_RETURN_METHOD: { + amqp_basic_return_t *m = (amqp_basic_return_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_return_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->reply_text, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_DELIVER_METHOD: { + amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_deliver_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_GET_METHOD: { + amqp_basic_get_t *m = + (amqp_basic_get_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_get_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_16(encoded, &offset, &m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->queue, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->no_ack = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_GET_OK_METHOD: { + amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_get_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0; + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->exchange, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->routing_key, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (!amqp_decode_32(encoded, &offset, &m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + *decoded = m; + return 0; + } + case AMQP_BASIC_GET_EMPTY_METHOD: { + amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_get_empty_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &m->cluster_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_ACK_METHOD: { + amqp_basic_ack_t *m = + (amqp_basic_ack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_ack_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_REJECT_METHOD: { + amqp_basic_reject_t *m = (amqp_basic_reject_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_reject_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_RECOVER_ASYNC_METHOD: { + amqp_basic_recover_async_t *m = + (amqp_basic_recover_async_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_async_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_RECOVER_METHOD: { + amqp_basic_recover_t *m = (amqp_basic_recover_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_BASIC_RECOVER_OK_METHOD: { + amqp_basic_recover_ok_t *m = (amqp_basic_recover_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_recover_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_BASIC_NACK_METHOD: { + amqp_basic_nack_t *m = + (amqp_basic_nack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_nack_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_64(encoded, &offset, &m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0; + m->requeue = (bit_buffer & (1 << 1)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_TX_SELECT_METHOD: { + amqp_tx_select_t *m = + (amqp_tx_select_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_select_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_TX_SELECT_OK_METHOD: { + amqp_tx_select_ok_t *m = (amqp_tx_select_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_select_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_TX_COMMIT_METHOD: { + amqp_tx_commit_t *m = + (amqp_tx_commit_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_commit_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_TX_COMMIT_OK_METHOD: { + amqp_tx_commit_ok_t *m = (amqp_tx_commit_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_commit_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_TX_ROLLBACK_METHOD: { + amqp_tx_rollback_t *m = (amqp_tx_rollback_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_rollback_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_TX_ROLLBACK_OK_METHOD: { + amqp_tx_rollback_ok_t *m = (amqp_tx_rollback_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_rollback_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + case AMQP_CONFIRM_SELECT_METHOD: { + amqp_confirm_select_t *m = (amqp_confirm_select_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_select_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + if (!amqp_decode_8(encoded, &offset, &bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0; + *decoded = m; + return 0; + } + case AMQP_CONFIRM_SELECT_OK_METHOD: { + amqp_confirm_select_ok_t *m = (amqp_confirm_select_ok_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_select_ok_t)); + if (m == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + *decoded = m; + return 0; + } + default: + return AMQP_STATUS_UNKNOWN_METHOD; + } +} + +int amqp_decode_properties(uint16_t class_id, amqp_pool_t *pool, + amqp_bytes_t encoded, void **decoded) { + size_t offset = 0; + + amqp_flags_t flags = 0; + int flagword_index = 0; + uint16_t partial_flags; + + do { + if (!amqp_decode_16(encoded, &offset, &partial_flags)) + return AMQP_STATUS_BAD_AMQP_DATA; + flags |= (partial_flags << (flagword_index * 16)); + flagword_index++; + } while (partial_flags & 1); + + switch (class_id) { + case 10: { + amqp_connection_properties_t *p = + (amqp_connection_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_connection_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 20: { + amqp_channel_properties_t *p = + (amqp_channel_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_channel_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 30: { + amqp_access_properties_t *p = (amqp_access_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_access_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 40: { + amqp_exchange_properties_t *p = + (amqp_exchange_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_exchange_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 50: { + amqp_queue_properties_t *p = (amqp_queue_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_queue_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 60: { + amqp_basic_properties_t *p = (amqp_basic_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_basic_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->content_type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->content_encoding, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_HEADERS_FLAG) { + { + int res = amqp_decode_table(encoded, pool, &(p->headers), &offset); + if (res < 0) return res; + } + } + if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { + if (!amqp_decode_8(encoded, &offset, &p->delivery_mode)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_PRIORITY_FLAG) { + if (!amqp_decode_8(encoded, &offset, &p->priority)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->correlation_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_REPLY_TO_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->reply_to, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_EXPIRATION_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->expiration, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->message_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_TIMESTAMP_FLAG) { + if (!amqp_decode_64(encoded, &offset, &p->timestamp)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_TYPE_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->type, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_USER_ID_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->user_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_APP_ID_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->app_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) { + { + uint8_t len; + if (!amqp_decode_8(encoded, &offset, &len) || + !amqp_decode_bytes(encoded, &offset, &p->cluster_id, len)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + } + *decoded = p; + return 0; + } + case 90: { + amqp_tx_properties_t *p = (amqp_tx_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_tx_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + case 85: { + amqp_confirm_properties_t *p = + (amqp_confirm_properties_t *)amqp_pool_alloc( + pool, sizeof(amqp_confirm_properties_t)); + if (p == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + p->_flags = flags; + *decoded = p; + return 0; + } + default: + return AMQP_STATUS_UNKNOWN_CLASS; + } +} + +int amqp_encode_method(amqp_method_number_t methodNumber, void *decoded, + amqp_bytes_t encoded) { + size_t offset = 0; + uint8_t bit_buffer; + + switch (methodNumber) { + case AMQP_CONNECTION_START_METHOD: { + amqp_connection_start_t *m = (amqp_connection_start_t *)decoded; + if (!amqp_encode_8(encoded, &offset, m->version_major)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_8(encoded, &offset, m->version_minor)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->server_properties), &offset); + if (res < 0) return res; + } + if (UINT32_MAX < m->mechanisms.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->mechanisms.len) || + !amqp_encode_bytes(encoded, &offset, m->mechanisms)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT32_MAX < m->locales.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->locales.len) || + !amqp_encode_bytes(encoded, &offset, m->locales)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_START_OK_METHOD: { + amqp_connection_start_ok_t *m = (amqp_connection_start_ok_t *)decoded; + { + int res = amqp_encode_table(encoded, &(m->client_properties), &offset); + if (res < 0) return res; + } + if (UINT8_MAX < m->mechanism.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->mechanism.len) || + !amqp_encode_bytes(encoded, &offset, m->mechanism)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT32_MAX < m->response.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) || + !amqp_encode_bytes(encoded, &offset, m->response)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->locale.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->locale.len) || + !amqp_encode_bytes(encoded, &offset, m->locale)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_SECURE_METHOD: { + amqp_connection_secure_t *m = (amqp_connection_secure_t *)decoded; + if (UINT32_MAX < m->challenge.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->challenge.len) || + !amqp_encode_bytes(encoded, &offset, m->challenge)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_SECURE_OK_METHOD: { + amqp_connection_secure_ok_t *m = (amqp_connection_secure_ok_t *)decoded; + if (UINT32_MAX < m->response.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) || + !amqp_encode_bytes(encoded, &offset, m->response)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_TUNE_METHOD: { + amqp_connection_tune_t *m = (amqp_connection_tune_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_TUNE_OK_METHOD: { + amqp_connection_tune_ok_t *m = (amqp_connection_tune_ok_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->channel_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->frame_max)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->heartbeat)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_OPEN_METHOD: { + amqp_connection_open_t *m = (amqp_connection_open_t *)decoded; + if (UINT8_MAX < m->virtual_host.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->virtual_host.len) || + !amqp_encode_bytes(encoded, &offset, m->virtual_host)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->capabilities.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->capabilities.len) || + !amqp_encode_bytes(encoded, &offset, m->capabilities)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->insist) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_OPEN_OK_METHOD: { + amqp_connection_open_ok_t *m = (amqp_connection_open_ok_t *)decoded; + if (UINT8_MAX < m->known_hosts.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->known_hosts.len) || + !amqp_encode_bytes(encoded, &offset, m->known_hosts)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_CLOSE_METHOD: { + amqp_connection_close_t *m = (amqp_connection_close_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_CLOSE_OK_METHOD: { + return (int)offset; + } + case AMQP_CONNECTION_BLOCKED_METHOD: { + amqp_connection_blocked_t *m = (amqp_connection_blocked_t *)decoded; + if (UINT8_MAX < m->reason.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reason.len) || + !amqp_encode_bytes(encoded, &offset, m->reason)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONNECTION_UNBLOCKED_METHOD: { + return (int)offset; + } + case AMQP_CHANNEL_OPEN_METHOD: { + amqp_channel_open_t *m = (amqp_channel_open_t *)decoded; + if (UINT8_MAX < m->out_of_band.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->out_of_band.len) || + !amqp_encode_bytes(encoded, &offset, m->out_of_band)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CHANNEL_OPEN_OK_METHOD: { + amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)decoded; + if (UINT32_MAX < m->channel_id.len || + !amqp_encode_32(encoded, &offset, (uint32_t)m->channel_id.len) || + !amqp_encode_bytes(encoded, &offset, m->channel_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CHANNEL_FLOW_METHOD: { + amqp_channel_flow_t *m = (amqp_channel_flow_t *)decoded; + bit_buffer = 0; + if (m->active) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CHANNEL_FLOW_OK_METHOD: { + amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)decoded; + bit_buffer = 0; + if (m->active) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CHANNEL_CLOSE_METHOD: { + amqp_channel_close_t *m = (amqp_channel_close_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->class_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->method_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CHANNEL_CLOSE_OK_METHOD: { + return (int)offset; + } + case AMQP_ACCESS_REQUEST_METHOD: { + amqp_access_request_t *m = (amqp_access_request_t *)decoded; + if (UINT8_MAX < m->realm.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->realm.len) || + !amqp_encode_bytes(encoded, &offset, m->realm)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->exclusive) bit_buffer |= (1 << 0); + if (m->passive) bit_buffer |= (1 << 1); + if (m->active) bit_buffer |= (1 << 2); + if (m->write) bit_buffer |= (1 << 3); + if (m->read) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_ACCESS_REQUEST_OK_METHOD: { + amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_EXCHANGE_DECLARE_METHOD: { + amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->type.len) || + !amqp_encode_bytes(encoded, &offset, m->type)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->passive) bit_buffer |= (1 << 0); + if (m->durable) bit_buffer |= (1 << 1); + if (m->auto_delete) bit_buffer |= (1 << 2); + if (m->internal) bit_buffer |= (1 << 3); + if (m->nowait) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_EXCHANGE_DECLARE_OK_METHOD: { + return (int)offset; + } + case AMQP_EXCHANGE_DELETE_METHOD: { + amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->if_unused) bit_buffer |= (1 << 0); + if (m->nowait) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_EXCHANGE_DELETE_OK_METHOD: { + return (int)offset; + } + case AMQP_EXCHANGE_BIND_METHOD: { + amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->destination.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) || + !amqp_encode_bytes(encoded, &offset, m->destination)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->source.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) || + !amqp_encode_bytes(encoded, &offset, m->source)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_EXCHANGE_BIND_OK_METHOD: { + return (int)offset; + } + case AMQP_EXCHANGE_UNBIND_METHOD: { + amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->destination.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) || + !amqp_encode_bytes(encoded, &offset, m->destination)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->source.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) || + !amqp_encode_bytes(encoded, &offset, m->source)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_EXCHANGE_UNBIND_OK_METHOD: { + return (int)offset; + } + case AMQP_QUEUE_DECLARE_METHOD: { + amqp_queue_declare_t *m = (amqp_queue_declare_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->passive) bit_buffer |= (1 << 0); + if (m->durable) bit_buffer |= (1 << 1); + if (m->exclusive) bit_buffer |= (1 << 2); + if (m->auto_delete) bit_buffer |= (1 << 3); + if (m->nowait) bit_buffer |= (1 << 4); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_QUEUE_DECLARE_OK_METHOD: { + amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)decoded; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->consumer_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_QUEUE_BIND_METHOD: { + amqp_queue_bind_t *m = (amqp_queue_bind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_QUEUE_BIND_OK_METHOD: { + return (int)offset; + } + case AMQP_QUEUE_PURGE_METHOD: { + amqp_queue_purge_t *m = (amqp_queue_purge_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_QUEUE_PURGE_OK_METHOD: { + amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_QUEUE_DELETE_METHOD: { + amqp_queue_delete_t *m = (amqp_queue_delete_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->if_unused) bit_buffer |= (1 << 0); + if (m->if_empty) bit_buffer |= (1 << 1); + if (m->nowait) bit_buffer |= (1 << 2); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_QUEUE_DELETE_OK_METHOD: { + amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_QUEUE_UNBIND_METHOD: { + amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_QUEUE_UNBIND_OK_METHOD: { + return (int)offset; + } + case AMQP_BASIC_QOS_METHOD: { + amqp_basic_qos_t *m = (amqp_basic_qos_t *)decoded; + if (!amqp_encode_32(encoded, &offset, m->prefetch_size)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_16(encoded, &offset, m->prefetch_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->global) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_QOS_OK_METHOD: { + return (int)offset; + } + case AMQP_BASIC_CONSUME_METHOD: { + amqp_basic_consume_t *m = (amqp_basic_consume_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->no_local) bit_buffer |= (1 << 0); + if (m->no_ack) bit_buffer |= (1 << 1); + if (m->exclusive) bit_buffer |= (1 << 2); + if (m->nowait) bit_buffer |= (1 << 3); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + { + int res = amqp_encode_table(encoded, &(m->arguments), &offset); + if (res < 0) return res; + } + return (int)offset; + } + case AMQP_BASIC_CONSUME_OK_METHOD: { + amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_CANCEL_METHOD: { + amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_CANCEL_OK_METHOD: { + amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_PUBLISH_METHOD: { + amqp_basic_publish_t *m = (amqp_basic_publish_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->mandatory) bit_buffer |= (1 << 0); + if (m->immediate) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RETURN_METHOD: { + amqp_basic_return_t *m = (amqp_basic_return_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->reply_code)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->reply_text.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) || + !amqp_encode_bytes(encoded, &offset, m->reply_text)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_DELIVER_METHOD: { + amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)decoded; + if (UINT8_MAX < m->consumer_tag.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) || + !amqp_encode_bytes(encoded, &offset, m->consumer_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->redelivered) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_GET_METHOD: { + amqp_basic_get_t *m = (amqp_basic_get_t *)decoded; + if (!amqp_encode_16(encoded, &offset, m->ticket)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->queue.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) || + !amqp_encode_bytes(encoded, &offset, m->queue)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->no_ack) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_GET_OK_METHOD: { + amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->redelivered) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->exchange.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) || + !amqp_encode_bytes(encoded, &offset, m->exchange)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (UINT8_MAX < m->routing_key.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) || + !amqp_encode_bytes(encoded, &offset, m->routing_key)) + return AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_encode_32(encoded, &offset, m->message_count)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_GET_EMPTY_METHOD: { + amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)decoded; + if (UINT8_MAX < m->cluster_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)m->cluster_id.len) || + !amqp_encode_bytes(encoded, &offset, m->cluster_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_ACK_METHOD: { + amqp_basic_ack_t *m = (amqp_basic_ack_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->multiple) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_REJECT_METHOD: { + amqp_basic_reject_t *m = (amqp_basic_reject_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RECOVER_ASYNC_METHOD: { + amqp_basic_recover_async_t *m = (amqp_basic_recover_async_t *)decoded; + bit_buffer = 0; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RECOVER_METHOD: { + amqp_basic_recover_t *m = (amqp_basic_recover_t *)decoded; + bit_buffer = 0; + if (m->requeue) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_BASIC_RECOVER_OK_METHOD: { + return (int)offset; + } + case AMQP_BASIC_NACK_METHOD: { + amqp_basic_nack_t *m = (amqp_basic_nack_t *)decoded; + if (!amqp_encode_64(encoded, &offset, m->delivery_tag)) + return AMQP_STATUS_BAD_AMQP_DATA; + bit_buffer = 0; + if (m->multiple) bit_buffer |= (1 << 0); + if (m->requeue) bit_buffer |= (1 << 1); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_TX_SELECT_METHOD: { + return (int)offset; + } + case AMQP_TX_SELECT_OK_METHOD: { + return (int)offset; + } + case AMQP_TX_COMMIT_METHOD: { + return (int)offset; + } + case AMQP_TX_COMMIT_OK_METHOD: { + return (int)offset; + } + case AMQP_TX_ROLLBACK_METHOD: { + return (int)offset; + } + case AMQP_TX_ROLLBACK_OK_METHOD: { + return (int)offset; + } + case AMQP_CONFIRM_SELECT_METHOD: { + amqp_confirm_select_t *m = (amqp_confirm_select_t *)decoded; + bit_buffer = 0; + if (m->nowait) bit_buffer |= (1 << 0); + if (!amqp_encode_8(encoded, &offset, bit_buffer)) + return AMQP_STATUS_BAD_AMQP_DATA; + return (int)offset; + } + case AMQP_CONFIRM_SELECT_OK_METHOD: { + return (int)offset; + } + default: + return AMQP_STATUS_UNKNOWN_METHOD; + } +} + +int amqp_encode_properties(uint16_t class_id, void *decoded, + amqp_bytes_t encoded) { + size_t offset = 0; + + /* Cheat, and get the flags out generically, relying on the + similarity of structure between classes */ + amqp_flags_t flags = *(amqp_flags_t *)decoded; /* cheating! */ + + { + /* We take a copy of flags to avoid destroying it, as it is used + in the autogenerated code below. */ + amqp_flags_t remaining_flags = flags; + do { + amqp_flags_t remainder = remaining_flags >> 16; + uint16_t partial_flags = remaining_flags & 0xFFFE; + if (remainder != 0) { + partial_flags |= 1; + } + if (!amqp_encode_16(encoded, &offset, partial_flags)) + return AMQP_STATUS_BAD_AMQP_DATA; + remaining_flags = remainder; + } while (remaining_flags != 0); + } + + switch (class_id) { + case 10: { + return (int)offset; + } + case 20: { + return (int)offset; + } + case 30: { + return (int)offset; + } + case 40: { + return (int)offset; + } + case 50: { + return (int)offset; + } + case 60: { + amqp_basic_properties_t *p = (amqp_basic_properties_t *)decoded; + if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { + if (UINT8_MAX < p->content_type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->content_type.len) || + !amqp_encode_bytes(encoded, &offset, p->content_type)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { + if (UINT8_MAX < p->content_encoding.len || + !amqp_encode_8(encoded, &offset, + (uint8_t)p->content_encoding.len) || + !amqp_encode_bytes(encoded, &offset, p->content_encoding)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_HEADERS_FLAG) { + { + int res = amqp_encode_table(encoded, &(p->headers), &offset); + if (res < 0) return res; + } + } + if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { + if (!amqp_encode_8(encoded, &offset, p->delivery_mode)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_PRIORITY_FLAG) { + if (!amqp_encode_8(encoded, &offset, p->priority)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) { + if (UINT8_MAX < p->correlation_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->correlation_id.len) || + !amqp_encode_bytes(encoded, &offset, p->correlation_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_REPLY_TO_FLAG) { + if (UINT8_MAX < p->reply_to.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->reply_to.len) || + !amqp_encode_bytes(encoded, &offset, p->reply_to)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_EXPIRATION_FLAG) { + if (UINT8_MAX < p->expiration.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->expiration.len) || + !amqp_encode_bytes(encoded, &offset, p->expiration)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) { + if (UINT8_MAX < p->message_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->message_id.len) || + !amqp_encode_bytes(encoded, &offset, p->message_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_TIMESTAMP_FLAG) { + if (!amqp_encode_64(encoded, &offset, p->timestamp)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_TYPE_FLAG) { + if (UINT8_MAX < p->type.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->type.len) || + !amqp_encode_bytes(encoded, &offset, p->type)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_USER_ID_FLAG) { + if (UINT8_MAX < p->user_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->user_id.len) || + !amqp_encode_bytes(encoded, &offset, p->user_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_APP_ID_FLAG) { + if (UINT8_MAX < p->app_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->app_id.len) || + !amqp_encode_bytes(encoded, &offset, p->app_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) { + if (UINT8_MAX < p->cluster_id.len || + !amqp_encode_8(encoded, &offset, (uint8_t)p->cluster_id.len) || + !amqp_encode_bytes(encoded, &offset, p->cluster_id)) + return AMQP_STATUS_BAD_AMQP_DATA; + } + return (int)offset; + } + case 90: { + return (int)offset; + } + case 85: { + return (int)offset; + } + default: + return AMQP_STATUS_UNKNOWN_CLASS; + } +} + +/** + * amqp_channel_open + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_channel_open_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_open_ok_t *AMQP_CALL + amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel) { + amqp_channel_open_t req; + req.out_of_band = amqp_empty_bytes; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_OPEN_METHOD, + AMQP_CHANNEL_OPEN_OK_METHOD, &req); +} + +/** + * amqp_channel_flow + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] active active + * @returns amqp_channel_flow_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_channel_flow_ok_t *AMQP_CALL + amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t active) { + amqp_channel_flow_t req; + req.active = active; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_FLOW_METHOD, + AMQP_CHANNEL_FLOW_OK_METHOD, &req); +} + +/** + * amqp_exchange_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] type type + * @param [in] passive passive + * @param [in] durable durable + * @param [in] auto_delete auto_delete + * @param [in] internal internal + * @param [in] arguments arguments + * @returns amqp_exchange_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_declare_ok_t *AMQP_CALL amqp_exchange_declare( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive, + amqp_boolean_t durable, amqp_boolean_t auto_delete, amqp_boolean_t internal, + amqp_table_t arguments) { + amqp_exchange_declare_t req; + req.ticket = 0; + req.exchange = exchange; + req.type = type; + req.passive = passive; + req.durable = durable; + req.auto_delete = auto_delete; + req.internal = internal; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DECLARE_METHOD, + AMQP_EXCHANGE_DECLARE_OK_METHOD, &req); +} + +/** + * amqp_exchange_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] exchange exchange + * @param [in] if_unused if_unused + * @returns amqp_exchange_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_delete_ok_t *AMQP_CALL + amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_boolean_t if_unused) { + amqp_exchange_delete_t req; + req.ticket = 0; + req.exchange = exchange; + req.if_unused = if_unused; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DELETE_METHOD, + AMQP_EXCHANGE_DELETE_OK_METHOD, &req); +} + +/** + * amqp_exchange_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_bind_ok_t *AMQP_CALL + amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_exchange_bind_t req; + req.ticket = 0; + req.destination = destination; + req.source = source; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_BIND_METHOD, + AMQP_EXCHANGE_BIND_OK_METHOD, &req); +} + +/** + * amqp_exchange_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] destination destination + * @param [in] source source + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_exchange_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_exchange_unbind_ok_t *AMQP_CALL + amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t destination, amqp_bytes_t source, + amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_exchange_unbind_t req; + req.ticket = 0; + req.destination = destination; + req.source = source; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_UNBIND_METHOD, + AMQP_EXCHANGE_UNBIND_OK_METHOD, &req); +} + +/** + * amqp_queue_declare + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] passive passive + * @param [in] durable durable + * @param [in] exclusive exclusive + * @param [in] auto_delete auto_delete + * @param [in] arguments arguments + * @returns amqp_queue_declare_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_declare_ok_t *AMQP_CALL amqp_queue_declare( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, + amqp_boolean_t auto_delete, amqp_table_t arguments) { + amqp_queue_declare_t req; + req.ticket = 0; + req.queue = queue; + req.passive = passive; + req.durable = durable; + req.exclusive = exclusive; + req.auto_delete = auto_delete; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DECLARE_METHOD, + AMQP_QUEUE_DECLARE_OK_METHOD, &req); +} + +/** + * amqp_queue_bind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_bind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_bind_ok_t *AMQP_CALL amqp_queue_bind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_queue_bind_t req; + req.ticket = 0; + req.queue = queue; + req.exchange = exchange; + req.routing_key = routing_key; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_BIND_METHOD, + AMQP_QUEUE_BIND_OK_METHOD, &req); +} + +/** + * amqp_queue_purge + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @returns amqp_queue_purge_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_purge_ok_t *AMQP_CALL amqp_queue_purge(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_bytes_t queue) { + amqp_queue_purge_t req; + req.ticket = 0; + req.queue = queue; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_PURGE_METHOD, + AMQP_QUEUE_PURGE_OK_METHOD, &req); +} + +/** + * amqp_queue_delete + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] if_unused if_unused + * @param [in] if_empty if_empty + * @returns amqp_queue_delete_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_delete_ok_t *AMQP_CALL amqp_queue_delete( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_boolean_t if_unused, amqp_boolean_t if_empty) { + amqp_queue_delete_t req; + req.ticket = 0; + req.queue = queue; + req.if_unused = if_unused; + req.if_empty = if_empty; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DELETE_METHOD, + AMQP_QUEUE_DELETE_OK_METHOD, &req); +} + +/** + * amqp_queue_unbind + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] exchange exchange + * @param [in] routing_key routing_key + * @param [in] arguments arguments + * @returns amqp_queue_unbind_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_queue_unbind_ok_t *AMQP_CALL amqp_queue_unbind( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) { + amqp_queue_unbind_t req; + req.ticket = 0; + req.queue = queue; + req.exchange = exchange; + req.routing_key = routing_key; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_UNBIND_METHOD, + AMQP_QUEUE_UNBIND_OK_METHOD, &req); +} + +/** + * amqp_basic_qos + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] prefetch_size prefetch_size + * @param [in] prefetch_count prefetch_count + * @param [in] global global + * @returns amqp_basic_qos_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_qos_ok_t *AMQP_CALL amqp_basic_qos(amqp_connection_state_t state, + amqp_channel_t channel, + uint32_t prefetch_size, + uint16_t prefetch_count, + amqp_boolean_t global) { + amqp_basic_qos_t req; + req.prefetch_size = prefetch_size; + req.prefetch_count = prefetch_count; + req.global = global; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_QOS_METHOD, + AMQP_BASIC_QOS_OK_METHOD, &req); +} + +/** + * amqp_basic_consume + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] queue queue + * @param [in] consumer_tag consumer_tag + * @param [in] no_local no_local + * @param [in] no_ack no_ack + * @param [in] exclusive exclusive + * @param [in] arguments arguments + * @returns amqp_basic_consume_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_consume_ok_t *AMQP_CALL amqp_basic_consume( + amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, + amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, + amqp_boolean_t exclusive, amqp_table_t arguments) { + amqp_basic_consume_t req; + req.ticket = 0; + req.queue = queue; + req.consumer_tag = consumer_tag; + req.no_local = no_local; + req.no_ack = no_ack; + req.exclusive = exclusive; + req.nowait = 0; + req.arguments = arguments; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CONSUME_METHOD, + AMQP_BASIC_CONSUME_OK_METHOD, &req); +} + +/** + * amqp_basic_cancel + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] consumer_tag consumer_tag + * @returns amqp_basic_cancel_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_cancel_ok_t *AMQP_CALL + amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t consumer_tag) { + amqp_basic_cancel_t req; + req.consumer_tag = consumer_tag; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CANCEL_METHOD, + AMQP_BASIC_CANCEL_OK_METHOD, &req); +} + +/** + * amqp_basic_recover + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @param [in] requeue requeue + * @returns amqp_basic_recover_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_basic_recover_ok_t *AMQP_CALL + amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel, + amqp_boolean_t requeue) { + amqp_basic_recover_t req; + req.requeue = requeue; + + return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_RECOVER_METHOD, + AMQP_BASIC_RECOVER_OK_METHOD, &req); +} + +/** + * amqp_tx_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_select_ok_t *AMQP_CALL amqp_tx_select(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_select_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_SELECT_METHOD, + AMQP_TX_SELECT_OK_METHOD, &req); +} + +/** + * amqp_tx_commit + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_commit_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_commit_ok_t *AMQP_CALL amqp_tx_commit(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_commit_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_COMMIT_METHOD, + AMQP_TX_COMMIT_OK_METHOD, &req); +} + +/** + * amqp_tx_rollback + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_tx_rollback_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_tx_rollback_ok_t *AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_tx_rollback_t req; + + return amqp_simple_rpc_decoded(state, channel, AMQP_TX_ROLLBACK_METHOD, + AMQP_TX_ROLLBACK_OK_METHOD, &req); +} + +/** + * amqp_confirm_select + * + * @param [in] state connection state + * @param [in] channel the channel to do the RPC on + * @returns amqp_confirm_select_ok_t + */ +AMQP_PUBLIC_FUNCTION +amqp_confirm_select_ok_t *AMQP_CALL + amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel) { + amqp_confirm_select_t req; + req.nowait = 0; + + return amqp_simple_rpc_decoded(state, channel, AMQP_CONFIRM_SELECT_METHOD, + AMQP_CONFIRM_SELECT_OK_METHOD, &req); +} diff --git a/ext/librabbitmq/centos_x64/include/amqp_framing.h b/ext/librabbitmq/librabbitmq/amqp_framing.h similarity index 100% rename from ext/librabbitmq/centos_x64/include/amqp_framing.h rename to ext/librabbitmq/librabbitmq/amqp_framing.h diff --git a/ext/librabbitmq/librabbitmq/amqp_hostcheck.c b/ext/librabbitmq/librabbitmq/amqp_hostcheck.c new file mode 100644 index 000000000..5a4f73399 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_hostcheck.c @@ -0,0 +1,195 @@ +/* + * Copyright 1996-2014 Daniel Stenberg . + * Copyright 2014 Michael Steinert + * + * All rights reserved. + * + * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +#include "amqp_hostcheck.h" + +#include + +/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() + * because its behavior is altered by the current locale. + */ + +static char amqp_raw_toupper(char in) { + switch (in) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + case 'g': + return 'G'; + case 'h': + return 'H'; + case 'i': + return 'I'; + case 'j': + return 'J'; + case 'k': + return 'K'; + case 'l': + return 'L'; + case 'm': + return 'M'; + case 'n': + return 'N'; + case 'o': + return 'O'; + case 'p': + return 'P'; + case 'q': + return 'Q'; + case 'r': + return 'R'; + case 's': + return 'S'; + case 't': + return 'T'; + case 'u': + return 'U'; + case 'v': + return 'V'; + case 'w': + return 'W'; + case 'x': + return 'X'; + case 'y': + return 'Y'; + case 'z': + return 'Z'; + } + return in; +} + +/* + * amqp_raw_equal() is for doing "raw" case insensitive strings. This is meant + * to be locale independent and only compare strings we know are safe for + * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for + * some further explanation to why this function is necessary. + * + * The function is capable of comparing a-z case insensitively even for + * non-ascii. + */ + +static int amqp_raw_equal(const char *first, const char *second) { + while (*first && *second) { + if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) { + /* get out of the loop as soon as they don't match */ + break; + } + first++; + second++; + } + /* we do the comparison here (possibly again), just to make sure that if + * the loop above is skipped because one of the strings reached zero, we + * must not return this as a successful match + */ + return (amqp_raw_toupper(*first) == amqp_raw_toupper(*second)); +} + +static int amqp_raw_nequal(const char *first, const char *second, size_t max) { + while (*first && *second && max) { + if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) { + break; + } + max--; + first++; + second++; + } + if (0 == max) { + return 1; /* they are equal this far */ + } + return amqp_raw_toupper(*first) == amqp_raw_toupper(*second); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * http://tools.ietf.org/html/rfc6125#section-6.4.3 + */ + +static amqp_hostcheck_result amqp_hostmatch(const char *hostname, + const char *pattern) { + const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; + int wildcard_enabled; + size_t prefixlen, suffixlen; + pattern_wildcard = strchr(pattern, '*'); + if (pattern_wildcard == NULL) { + return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH + : AMQP_HCR_NO_MATCH; + } + /* We require at least 2 dots in pattern to avoid too wide wildcard match. */ + wildcard_enabled = 1; + pattern_label_end = strchr(pattern, '.'); + if (pattern_label_end == NULL || strchr(pattern_label_end + 1, '.') == NULL || + pattern_wildcard > pattern_label_end || + amqp_raw_nequal(pattern, "xn--", 4)) { + wildcard_enabled = 0; + } + if (!wildcard_enabled) { + return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH + : AMQP_HCR_NO_MATCH; + } + hostname_label_end = strchr(hostname, '.'); + if (hostname_label_end == NULL || + !amqp_raw_equal(pattern_label_end, hostname_label_end)) { + return AMQP_HCR_NO_MATCH; + } + /* The wildcard must match at least one character, so the left-most + * label of the hostname is at least as large as the left-most label + * of the pattern. + */ + if (hostname_label_end - hostname < pattern_label_end - pattern) { + return AMQP_HCR_NO_MATCH; + } + prefixlen = pattern_wildcard - pattern; + suffixlen = pattern_label_end - (pattern_wildcard + 1); + return amqp_raw_nequal(pattern, hostname, prefixlen) && + amqp_raw_nequal(pattern_wildcard + 1, + hostname_label_end - suffixlen, suffixlen) + ? AMQP_HCR_MATCH + : AMQP_HCR_NO_MATCH; +} + +amqp_hostcheck_result amqp_hostcheck(const char *match_pattern, + const char *hostname) { + /* sanity check */ + if (!match_pattern || !*match_pattern || !hostname || !*hostname) { + return AMQP_HCR_NO_MATCH; + } + /* trivial case */ + if (amqp_raw_equal(hostname, match_pattern)) { + return AMQP_HCR_MATCH; + } + return amqp_hostmatch(hostname, match_pattern); +} diff --git a/ext/librabbitmq/librabbitmq/amqp_hostcheck.h b/ext/librabbitmq/librabbitmq/amqp_hostcheck.h new file mode 100644 index 000000000..7ab5c267b --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_hostcheck.h @@ -0,0 +1,48 @@ +#ifndef librabbitmq_amqp_hostcheck_h +#define librabbitmq_amqp_hostcheck_h + +/* + * Copyright 1996-2014 Daniel Stenberg . + * Copyright 2014 Michael Steinert + * + * All rights reserved. + * + * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +typedef enum { + AMQP_HCR_NO_MATCH = 0, + AMQP_HCR_MATCH = 1 +} amqp_hostcheck_result; + +/** + * Determine whether hostname matches match_pattern. + * + * match_pattern may include wildcards. + * + * Match is performed based on the rules set forth in RFC6125 section 6.4.3. + * http://tools.ietf.org/html/rfc6125#section-6.4.3 + * + * \param match_pattern RFC6125 compliant pattern + * \param hostname to match against + * \returns AMQP_HCR_MATCH if its a match, AMQP_HCR_NO_MATCH otherwise. + */ +amqp_hostcheck_result amqp_hostcheck(const char *match_pattern, + const char *hostname); + +#endif diff --git a/ext/librabbitmq/librabbitmq/amqp_mem.c b/ext/librabbitmq/librabbitmq/amqp_mem.c new file mode 100644 index 000000000..f0d47cc73 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_mem.c @@ -0,0 +1,242 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_private.h" +#include +#include +#include +#include +#include +#include + +char const *amqp_version(void) { return AMQP_VERSION_STRING; } + +uint32_t amqp_version_number(void) { return AMQP_VERSION; } + +void init_amqp_pool(amqp_pool_t *pool, size_t pagesize) { + pool->pagesize = pagesize ? pagesize : 4096; + + pool->pages.num_blocks = 0; + pool->pages.blocklist = NULL; + + pool->large_blocks.num_blocks = 0; + pool->large_blocks.blocklist = NULL; + + pool->next_page = 0; + pool->alloc_block = NULL; + pool->alloc_used = 0; +} + +static void empty_blocklist(amqp_pool_blocklist_t *x) { + int i; + + if (x->blocklist != NULL) { + for (i = 0; i < x->num_blocks; i++) { + free(x->blocklist[i]); + } + free(x->blocklist); + } + x->num_blocks = 0; + x->blocklist = NULL; +} + +void recycle_amqp_pool(amqp_pool_t *pool) { + empty_blocklist(&pool->large_blocks); + pool->next_page = 0; + pool->alloc_block = NULL; + pool->alloc_used = 0; +} + +void empty_amqp_pool(amqp_pool_t *pool) { + recycle_amqp_pool(pool); + empty_blocklist(&pool->pages); +} + +/* Returns 1 on success, 0 on failure */ +static int record_pool_block(amqp_pool_blocklist_t *x, void *block) { + size_t blocklistlength = sizeof(void *) * (x->num_blocks + 1); + + if (x->blocklist == NULL) { + x->blocklist = malloc(blocklistlength); + if (x->blocklist == NULL) { + return 0; + } + } else { + void *newbl = realloc(x->blocklist, blocklistlength); + if (newbl == NULL) { + return 0; + } + x->blocklist = newbl; + } + + x->blocklist[x->num_blocks] = block; + x->num_blocks++; + return 1; +} + +void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) { + if (amount == 0) { + return NULL; + } + + amount = (amount + 7) & (~7); /* round up to nearest 8-byte boundary */ + + if (amount > pool->pagesize) { + void *result = calloc(1, amount); + if (result == NULL) { + return NULL; + } + if (!record_pool_block(&pool->large_blocks, result)) { + free(result); + return NULL; + } + return result; + } + + if (pool->alloc_block != NULL) { + assert(pool->alloc_used <= pool->pagesize); + + if (pool->alloc_used + amount <= pool->pagesize) { + void *result = pool->alloc_block + pool->alloc_used; + pool->alloc_used += amount; + return result; + } + } + + if (pool->next_page >= pool->pages.num_blocks) { + pool->alloc_block = calloc(1, pool->pagesize); + if (pool->alloc_block == NULL) { + return NULL; + } + if (!record_pool_block(&pool->pages, pool->alloc_block)) { + return NULL; + } + pool->next_page = pool->pages.num_blocks; + } else { + pool->alloc_block = pool->pages.blocklist[pool->next_page]; + pool->next_page++; + } + + pool->alloc_used = amount; + + return pool->alloc_block; +} + +void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, + amqp_bytes_t *output) { + output->len = amount; + output->bytes = amqp_pool_alloc(pool, amount); +} + +amqp_bytes_t amqp_cstring_bytes(char const *cstr) { + amqp_bytes_t result; + result.len = strlen(cstr); + result.bytes = (void *)cstr; + return result; +} + +amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) { + amqp_bytes_t result; + result.len = src.len; + result.bytes = malloc(src.len); + if (result.bytes != NULL) { + memcpy(result.bytes, src.bytes, src.len); + } + return result; +} + +amqp_bytes_t amqp_bytes_malloc(size_t amount) { + amqp_bytes_t result; + result.len = amount; + result.bytes = malloc(amount); /* will return NULL if it fails */ + return result; +} + +void amqp_bytes_free(amqp_bytes_t bytes) { free(bytes.bytes); } + +amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_pool_table_entry_t *entry; + size_t index = channel % POOL_TABLE_SIZE; + + entry = state->pool_table[index]; + + for (; NULL != entry; entry = entry->next) { + if (channel == entry->channel) { + return &entry->pool; + } + } + + entry = malloc(sizeof(amqp_pool_table_entry_t)); + if (NULL == entry) { + return NULL; + } + + entry->channel = channel; + entry->next = state->pool_table[index]; + state->pool_table[index] = entry; + + init_amqp_pool(&entry->pool, state->frame_max); + + return &entry->pool; +} + +amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state, + amqp_channel_t channel) { + amqp_pool_table_entry_t *entry; + size_t index = channel % POOL_TABLE_SIZE; + + entry = state->pool_table[index]; + + for (; NULL != entry; entry = entry->next) { + if (channel == entry->channel) { + return &entry->pool; + } + } + + return NULL; +} + +int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l) { + if (r.len == l.len && + (r.bytes == l.bytes || 0 == memcmp(r.bytes, l.bytes, r.len))) { + return 1; + } + return 0; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_openssl.c b/ext/librabbitmq/librabbitmq/amqp_openssl.c new file mode 100644 index 000000000..bcd5ba53d --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_openssl.c @@ -0,0 +1,704 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2012-2014 Alan Antonuk. + * All Rights Reserved. + * + * Portions created by Michael Steinert are Copyright (c) 2012-2014 Michael + * Steinert. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_openssl_bio.h" +#include "amqp_openssl_hostname_validation.h" +#include "amqp_private.h" +#include "amqp_socket.h" +#include "amqp_ssl_socket.h" +#include "amqp_time.h" +#include "threads.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int initialize_ssl_and_increment_connections(void); +static int decrement_ssl_connections(void); + +static unsigned long ssl_threadid_callback(void); +static void ssl_locking_callback(int mode, int n, const char *file, int line); +static pthread_mutex_t *amqp_openssl_lockarray = NULL; + +static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER; +static amqp_boolean_t do_initialize_openssl = 1; +static amqp_boolean_t openssl_initialized = 0; +static amqp_boolean_t openssl_bio_initialized = 0; +static int openssl_connections = 0; + +#define CHECK_SUCCESS(condition) \ + do { \ + int check_success_ret = (condition); \ + if (check_success_ret) { \ + amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \ + strerror(check_success_ret)); \ + } \ + } while (0) + +struct amqp_ssl_socket_t { + const struct amqp_socket_class_t *klass; + SSL_CTX *ctx; + int sockfd; + SSL *ssl; + amqp_boolean_t verify_peer; + amqp_boolean_t verify_hostname; + int internal_error; +}; + +static ssize_t amqp_ssl_socket_send(void *base, const void *buf, size_t len, + AMQP_UNUSED int flags) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + int res; + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + + /* SSL_write takes an int for length of buffer, protect against len being + * larger than larger than what SSL_write can take */ + if (len > INT_MAX) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + ERR_clear_error(); + self->internal_error = 0; + + /* This will only return on error, or once the whole buffer has been + * written to the SSL stream. See SSL_MODE_ENABLE_PARTIAL_WRITE */ + res = SSL_write(self->ssl, buf, (int)len); + if (0 >= res) { + self->internal_error = SSL_get_error(self->ssl, res); + /* TODO: Close connection if it isn't already? */ + /* TODO: Possibly be more intelligent in reporting WHAT went wrong */ + switch (self->internal_error) { + case SSL_ERROR_WANT_READ: + res = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD; + break; + case SSL_ERROR_WANT_WRITE: + res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE; + break; + case SSL_ERROR_ZERO_RETURN: + res = AMQP_STATUS_CONNECTION_CLOSED; + break; + default: + res = AMQP_STATUS_SSL_ERROR; + break; + } + } else { + self->internal_error = 0; + } + + return (ssize_t)res; +} + +static ssize_t amqp_ssl_socket_recv(void *base, void *buf, size_t len, + AMQP_UNUSED int flags) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + int received; + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + + /* SSL_read takes an int for length of buffer, protect against len being + * larger than larger than what SSL_read can take */ + if (len > INT_MAX) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + ERR_clear_error(); + self->internal_error = 0; + + received = SSL_read(self->ssl, buf, (int)len); + if (0 >= received) { + self->internal_error = SSL_get_error(self->ssl, received); + switch (self->internal_error) { + case SSL_ERROR_WANT_READ: + received = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD; + break; + case SSL_ERROR_WANT_WRITE: + received = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE; + break; + case SSL_ERROR_ZERO_RETURN: + received = AMQP_STATUS_CONNECTION_CLOSED; + break; + default: + received = AMQP_STATUS_SSL_ERROR; + break; + } + } + + return (ssize_t)received; +} + +static int amqp_ssl_socket_open(void *base, const char *host, int port, + struct timeval *timeout) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + long result; + int status; + amqp_time_t deadline; + X509 *cert; + BIO *bio; + if (-1 != self->sockfd) { + return AMQP_STATUS_SOCKET_INUSE; + } + ERR_clear_error(); + + self->ssl = SSL_new(self->ctx); + if (!self->ssl) { + self->internal_error = ERR_peek_error(); + status = AMQP_STATUS_SSL_ERROR; + goto exit; + } + + status = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != status) { + return status; + } + + self->sockfd = amqp_open_socket_inner(host, port, deadline); + if (0 > self->sockfd) { + status = self->sockfd; + self->internal_error = amqp_os_socket_error(); + self->sockfd = -1; + goto error_out1; + } + + bio = BIO_new(amqp_openssl_bio()); + if (!bio) { + status = AMQP_STATUS_NO_MEMORY; + goto error_out2; + } + + BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE); + SSL_set_bio(self->ssl, bio, bio); + + status = SSL_set_tlsext_host_name(self->ssl, host); + if (!status) { + self->internal_error = SSL_get_error(self->ssl, status); + status = AMQP_STATUS_SSL_ERROR; + goto error_out2; + } + +start_connect: + status = SSL_connect(self->ssl); + if (status != 1) { + self->internal_error = SSL_get_error(self->ssl, status); + switch (self->internal_error) { + case SSL_ERROR_WANT_READ: + status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline); + break; + case SSL_ERROR_WANT_WRITE: + status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline); + break; + default: + status = AMQP_STATUS_SSL_CONNECTION_FAILED; + } + if (AMQP_STATUS_OK == status) { + goto start_connect; + } + goto error_out2; + } + + cert = SSL_get_peer_certificate(self->ssl); + + if (self->verify_peer) { + if (!cert) { + self->internal_error = 0; + status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED; + goto error_out3; + } + + result = SSL_get_verify_result(self->ssl); + if (X509_V_OK != result) { + self->internal_error = result; + status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED; + goto error_out4; + } + } + if (self->verify_hostname) { + if (!cert) { + self->internal_error = 0; + status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED; + goto error_out3; + } + + if (AMQP_HVR_MATCH_FOUND != amqp_ssl_validate_hostname(host, cert)) { + self->internal_error = 0; + status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED; + goto error_out4; + } + } + + X509_free(cert); + self->internal_error = 0; + status = AMQP_STATUS_OK; + +exit: + return status; + +error_out4: + X509_free(cert); +error_out3: + SSL_shutdown(self->ssl); +error_out2: + amqp_os_socket_close(self->sockfd); + self->sockfd = -1; +error_out1: + SSL_free(self->ssl); + self->ssl = NULL; + goto exit; +} + +static int amqp_ssl_socket_close(void *base, amqp_socket_close_enum force) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + + if (AMQP_SC_NONE == force) { + /* don't try too hard to shutdown the connection */ + SSL_shutdown(self->ssl); + } + + SSL_free(self->ssl); + self->ssl = NULL; + + if (amqp_os_socket_close(self->sockfd)) { + return AMQP_STATUS_SOCKET_ERROR; + } + self->sockfd = -1; + + return AMQP_STATUS_OK; +} + +static int amqp_ssl_socket_get_sockfd(void *base) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + return self->sockfd; +} + +static void amqp_ssl_socket_delete(void *base) { + struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; + + if (self) { + amqp_ssl_socket_close(self, AMQP_SC_NONE); + + SSL_CTX_free(self->ctx); + free(self); + } + decrement_ssl_connections(); +} + +static const struct amqp_socket_class_t amqp_ssl_socket_class = { + amqp_ssl_socket_send, /* send */ + amqp_ssl_socket_recv, /* recv */ + amqp_ssl_socket_open, /* open */ + amqp_ssl_socket_close, /* close */ + amqp_ssl_socket_get_sockfd, /* get_sockfd */ + amqp_ssl_socket_delete /* delete */ +}; + +amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) { + struct amqp_ssl_socket_t *self = calloc(1, sizeof(*self)); + int status; + if (!self) { + return NULL; + } + + self->sockfd = -1; + self->klass = &amqp_ssl_socket_class; + self->verify_peer = 1; + self->verify_hostname = 1; + + status = initialize_ssl_and_increment_connections(); + if (status) { + goto error; + } + + self->ctx = SSL_CTX_new(SSLv23_client_method()); + if (!self->ctx) { + goto error; + } + /* Disable SSLv2 and SSLv3 */ + SSL_CTX_set_options(self->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + amqp_set_socket(state, (amqp_socket_t *)self); + + return (amqp_socket_t *)self; +error: + amqp_ssl_socket_delete((amqp_socket_t *)self); + return NULL; +} + +int amqp_ssl_socket_set_cacert(amqp_socket_t *base, const char *cacert) { + int status; + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + status = SSL_CTX_load_verify_locations(self->ctx, cacert, NULL); + if (1 != status) { + return AMQP_STATUS_SSL_ERROR; + } + return AMQP_STATUS_OK; +} + +int amqp_ssl_socket_set_key(amqp_socket_t *base, const char *cert, + const char *key) { + int status; + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + status = SSL_CTX_use_certificate_chain_file(self->ctx, cert); + if (1 != status) { + return AMQP_STATUS_SSL_ERROR; + } + status = SSL_CTX_use_PrivateKey_file(self->ctx, key, SSL_FILETYPE_PEM); + if (1 != status) { + return AMQP_STATUS_SSL_ERROR; + } + return AMQP_STATUS_OK; +} + +static int password_cb(AMQP_UNUSED char *buffer, AMQP_UNUSED int length, + AMQP_UNUSED int rwflag, AMQP_UNUSED void *user_data) { + amqp_abort("rabbitmq-c does not support password protected keys"); +} + +int amqp_ssl_socket_set_key_buffer(amqp_socket_t *base, const char *cert, + const void *key, size_t n) { + int status = AMQP_STATUS_OK; + BIO *buf = NULL; + RSA *rsa = NULL; + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + if (n > INT_MAX) { + return AMQP_STATUS_INVALID_PARAMETER; + } + self = (struct amqp_ssl_socket_t *)base; + status = SSL_CTX_use_certificate_chain_file(self->ctx, cert); + if (1 != status) { + return AMQP_STATUS_SSL_ERROR; + } + buf = BIO_new_mem_buf((void *)key, (int)n); + if (!buf) { + goto error; + } + rsa = PEM_read_bio_RSAPrivateKey(buf, NULL, password_cb, NULL); + if (!rsa) { + goto error; + } + status = SSL_CTX_use_RSAPrivateKey(self->ctx, rsa); + if (1 != status) { + goto error; + } +exit: + BIO_vfree(buf); + RSA_free(rsa); + return status; +error: + status = AMQP_STATUS_SSL_ERROR; + goto exit; +} + +int amqp_ssl_socket_set_cert(amqp_socket_t *base, const char *cert) { + int status; + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + status = SSL_CTX_use_certificate_chain_file(self->ctx, cert); + if (1 != status) { + return AMQP_STATUS_SSL_ERROR; + } + return AMQP_STATUS_OK; +} + +void amqp_ssl_socket_set_verify(amqp_socket_t *base, amqp_boolean_t verify) { + amqp_ssl_socket_set_verify_peer(base, verify); + amqp_ssl_socket_set_verify_hostname(base, verify); +} + +void amqp_ssl_socket_set_verify_peer(amqp_socket_t *base, + amqp_boolean_t verify) { + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + self->verify_peer = verify; +} + +void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *base, + amqp_boolean_t verify) { + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + self->verify_hostname = verify; +} + +int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base, + amqp_tls_version_t min, + amqp_tls_version_t max) { + struct amqp_ssl_socket_t *self; + if (base->klass != &amqp_ssl_socket_class) { + amqp_abort("<%p> is not of type amqp_ssl_socket_t", base); + } + self = (struct amqp_ssl_socket_t *)base; + + { + long clear_options; + long set_options = 0; +#if defined(SSL_OP_NO_TLSv1_2) + amqp_tls_version_t max_supported = AMQP_TLSv1_2; + clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; +#elif defined(SSL_OP_NO_TLSv1_1) + amqp_tls_version_t max_supported = AMQP_TLSv1_1; + clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; +#elif defined(SSL_OP_NO_TLSv1) + amqp_tls_version_t max_supported = AMQP_TLSv1; + clear_options = SSL_OP_NO_TLSv1; +#else +#error "Need a version of OpenSSL that can support TLSv1 or greater." +#endif + + if (AMQP_TLSvLATEST == max) { + max = max_supported; + } + if (AMQP_TLSvLATEST == min) { + min = max_supported; + } + + if (min > max) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + if (max > max_supported || min > max_supported) { + return AMQP_STATUS_UNSUPPORTED; + } + + if (min > AMQP_TLSv1) { + set_options |= SSL_OP_NO_TLSv1; + } +#ifdef SSL_OP_NO_TLSv1_1 + if (min > AMQP_TLSv1_1 || max < AMQP_TLSv1_1) { + set_options |= SSL_OP_NO_TLSv1_1; + } +#endif +#ifdef SSL_OP_NO_TLSv1_2 + if (max < AMQP_TLSv1_2) { + set_options |= SSL_OP_NO_TLSv1_2; + } +#endif + SSL_CTX_clear_options(self->ctx, clear_options); + SSL_CTX_set_options(self->ctx, set_options); + } + + return AMQP_STATUS_OK; +} + +void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) { + CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex)); + + if (openssl_connections == 0 && !openssl_initialized) { + do_initialize_openssl = do_initialize; + } + CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex)); +} + +static unsigned long ssl_threadid_callback(void) { + return (unsigned long)pthread_self(); +} + +static void ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file, + AMQP_UNUSED int line) { + if (mode & CRYPTO_LOCK) { + CHECK_SUCCESS(pthread_mutex_lock(&amqp_openssl_lockarray[n])); + } else { + CHECK_SUCCESS(pthread_mutex_unlock(&amqp_openssl_lockarray[n])); + } +} + +static int setup_openssl(void) { + int status; + + int i; + amqp_openssl_lockarray = calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t)); + if (!amqp_openssl_lockarray) { + status = AMQP_STATUS_NO_MEMORY; + goto out; + } + for (i = 0; i < CRYPTO_num_locks(); i++) { + if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) { + int j; + for (j = 0; j < i; j++) { + pthread_mutex_destroy(&amqp_openssl_lockarray[j]); + } + free(amqp_openssl_lockarray); + status = AMQP_STATUS_SSL_ERROR; + goto out; + } + } + CRYPTO_set_id_callback(ssl_threadid_callback); + CRYPTO_set_locking_callback(ssl_locking_callback); + +#ifdef AMQP_OPENSSL_V110 + if (CONF_modules_load_file(NULL, "rabbitmq-c", CONF_MFLAGS_DEFAULT_SECTION) <= + 0) { + status = AMQP_STATUS_SSL_ERROR; + goto out; + } +#else + OPENSSL_config(NULL); +#endif + SSL_library_init(); + SSL_load_error_strings(); + + status = AMQP_STATUS_OK; +out: + return status; +} + +int amqp_initialize_ssl_library(void) { + int status; + CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex)); + + if (!openssl_initialized) { + status = setup_openssl(); + if (status) { + goto out; + } + openssl_initialized = 1; + } + + status = AMQP_STATUS_OK; +out: + CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex)); + return status; +} + +static int initialize_ssl_and_increment_connections() { + int status; + CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex)); + + if (do_initialize_openssl && !openssl_initialized) { + status = setup_openssl(); + if (status) { + goto exit; + } + openssl_initialized = 1; + } + + if (!openssl_bio_initialized) { + status = amqp_openssl_bio_init(); + if (status) { + goto exit; + } + openssl_bio_initialized = 1; + } + + openssl_connections += 1; + status = AMQP_STATUS_OK; +exit: + CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex)); + return status; +} + +static int decrement_ssl_connections(void) { + CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex)); + + if (openssl_connections > 0) { + openssl_connections--; + } + + CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex)); + return AMQP_STATUS_OK; +} + +int amqp_uninitialize_ssl_library(void) { + int status; + CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex)); + + if (openssl_connections > 0) { + status = AMQP_STATUS_SOCKET_INUSE; + goto out; + } + + amqp_openssl_bio_destroy(); + openssl_bio_initialized = 0; + +#ifndef AMQP_OPENSSL_V110 + ERR_remove_state(0); +#endif + +#ifndef LIBRESSL_VERSION_NUMBER + FIPS_mode_set(0); +#endif + + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + { + int i; + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_destroy(&amqp_openssl_lockarray[i]); + } + free(amqp_openssl_lockarray); + } + + ENGINE_cleanup(); + CONF_modules_free(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); +#if (OPENSSL_VERSION_NUMBER >= 0x10002003L) && !defined(LIBRESSL_VERSION_NUMBER) + SSL_COMP_free_compression_methods(); +#endif + + openssl_initialized = 0; + + status = AMQP_STATUS_OK; +out: + CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex)); + return status; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_openssl_bio.c b/ext/librabbitmq/librabbitmq/amqp_openssl_bio.c new file mode 100644 index 000000000..3556d6f3a --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_openssl_bio.c @@ -0,0 +1,193 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "amqp_openssl_bio.h" +#include "amqp_socket.h" + +#include +#include +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#include +#endif + +#ifdef MSG_NOSIGNAL +#define AMQP_USE_AMQP_BIO +#endif + +static int amqp_ssl_bio_initialized = 0; + +#ifdef AMQP_USE_AMQP_BIO + +static BIO_METHOD *amqp_bio_method; + +static int amqp_openssl_bio_should_retry(int res) { + if (res == -1) { + int err = amqp_os_socket_error(); + if ( +#ifdef EWOULDBLOCK + err == EWOULDBLOCK || +#endif +#ifdef WSAEWOULDBLOCK + err == WSAEWOULDBLOCK || +#endif +#ifdef ENOTCONN + err == ENOTCONN || +#endif +#ifdef EINTR + err == EINTR || +#endif +#ifdef EAGAIN + err == EAGAIN || +#endif +#ifdef EPROTO + err == EPROTO || +#endif +#ifdef EINPROGRESS + err == EINPROGRESS || +#endif +#ifdef EALREADY + err == EALREADY || +#endif + 0) { + return 1; + } + } + return 0; +} + +static int amqp_openssl_bio_write(BIO *b, const char *in, int inl) { + int flags = 0; + int fd; + int res; + +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + + BIO_get_fd(b, &fd); + res = send(fd, in, inl, flags); + + BIO_clear_retry_flags(b); + if (res <= 0 && amqp_openssl_bio_should_retry(res)) { + BIO_set_retry_write(b); + } + + return res; +} + +static int amqp_openssl_bio_read(BIO *b, char *out, int outl) { + int flags = 0; + int fd; + int res; + +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + + BIO_get_fd(b, &fd); + res = recv(fd, out, outl, flags); + + BIO_clear_retry_flags(b); + if (res <= 0 && amqp_openssl_bio_should_retry(res)) { + BIO_set_retry_read(b); + } + + return res; +} + +#ifndef AMQP_OPENSSL_V110 +static int BIO_meth_set_write(BIO_METHOD *biom, + int (*wfn)(BIO *, const char *, int)) { + biom->bwrite = wfn; + return 0; +} + +static int BIO_meth_set_read(BIO_METHOD *biom, int (*rfn)(BIO *, char *, int)) { + biom->bread = rfn; + return 0; +} +#endif /* AQP_OPENSSL_V110 */ +#endif /* AMQP_USE_AMQP_BIO */ + +int amqp_openssl_bio_init(void) { + assert(!amqp_ssl_bio_initialized); +#ifdef AMQP_USE_AMQP_BIO +#ifdef AMQP_OPENSSL_V110 + if (!(amqp_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "amqp_bio_method"))) { + return AMQP_STATUS_NO_MEMORY; + } + + // casting away const is necessary until + // https://github.com/openssl/openssl/pull/2181/, which is targeted for + // openssl 1.1.1 + BIO_METHOD *meth = (BIO_METHOD *)BIO_s_socket(); + BIO_meth_set_create(amqp_bio_method, BIO_meth_get_create(meth)); + BIO_meth_set_destroy(amqp_bio_method, BIO_meth_get_destroy(meth)); + BIO_meth_set_ctrl(amqp_bio_method, BIO_meth_get_ctrl(meth)); + BIO_meth_set_callback_ctrl(amqp_bio_method, BIO_meth_get_callback_ctrl(meth)); + BIO_meth_set_read(amqp_bio_method, BIO_meth_get_read(meth)); + BIO_meth_set_write(amqp_bio_method, BIO_meth_get_write(meth)); + BIO_meth_set_gets(amqp_bio_method, BIO_meth_get_gets(meth)); + BIO_meth_set_puts(amqp_bio_method, BIO_meth_get_puts(meth)); +#else + if (!(amqp_bio_method = OPENSSL_malloc(sizeof(BIO_METHOD)))) { + return AMQP_STATUS_NO_MEMORY; + } + + memcpy(amqp_bio_method, BIO_s_socket(), sizeof(BIO_METHOD)); +#endif + BIO_meth_set_write(amqp_bio_method, amqp_openssl_bio_write); + BIO_meth_set_read(amqp_bio_method, amqp_openssl_bio_read); +#endif + + amqp_ssl_bio_initialized = 1; + return AMQP_STATUS_OK; +} + +void amqp_openssl_bio_destroy(void) { + assert(amqp_ssl_bio_initialized); +#ifdef AMQP_USE_AMQP_BIO +#ifdef AMQP_OPENSSL_V110 + BIO_meth_free(amqp_bio_method); +#else + OPENSSL_free(amqp_bio_method); +#endif + amqp_bio_method = NULL; +#endif + amqp_ssl_bio_initialized = 0; +} + +BIO_METHOD_PTR amqp_openssl_bio(void) { + assert(amqp_ssl_bio_initialized); +#ifdef AMQP_USE_AMQP_BIO + return amqp_bio_method; +#else + return BIO_s_socket(); +#endif +} diff --git a/ext/librabbitmq/librabbitmq/amqp_openssl_bio.h b/ext/librabbitmq/librabbitmq/amqp_openssl_bio.h new file mode 100644 index 000000000..ec09c5e7a --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_openssl_bio.h @@ -0,0 +1,44 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef AMQP_OPENSSL_BIO +#define AMQP_OPENSSL_BIO + +#include + +int amqp_openssl_bio_init(void); + +void amqp_openssl_bio_destroy(void); + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#define AMQP_OPENSSL_V110 +#endif + +#ifdef AMQP_OPENSSL_V110 +typedef const BIO_METHOD *BIO_METHOD_PTR; +#else +typedef BIO_METHOD *BIO_METHOD_PTR; +#endif + +BIO_METHOD_PTR amqp_openssl_bio(void); + +#endif /* ifndef AMQP_OPENSSL_BIO */ diff --git a/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c b/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c new file mode 100644 index 000000000..133d73c89 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012, iSEC Partners. + * Copyright (C) 2015 Alan Antonuk. + * + * All rights reserved. + * + * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +/* Originally from: + * https://github.com/iSECPartners/ssl-conservatory + * https://wiki.openssl.org/index.php/Hostname_validation + */ + +#include +#include + +#include "amqp_hostcheck.h" +#include "amqp_openssl_bio.h" +#include "amqp_openssl_hostname_validation.h" + +#include + +#define HOSTNAME_MAX_SIZE 255 + +/** + * Tries to find a match for hostname in the certificate's Common Name field. + * + * Returns AMQP_HVR_MATCH_FOUND if a match was found. + * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. + * Returns AMQP_HVR_MALFORMED_CERTIFICATE if the Common Name had a NUL character + * embedded in it. + * Returns AMQP_HVR_ERROR if the Common Name could not be extracted. + */ +static amqp_hostname_validation_result amqp_matches_common_name( + const char *hostname, const X509 *server_cert) { + int common_name_loc = -1; + X509_NAME_ENTRY *common_name_entry = NULL; + ASN1_STRING *common_name_asn1 = NULL; + const char *common_name_str = NULL; + + // Find the position of the CN field in the Subject field of the certificate + common_name_loc = X509_NAME_get_index_by_NID( + X509_get_subject_name((X509 *)server_cert), NID_commonName, -1); + if (common_name_loc < 0) { + return AMQP_HVR_ERROR; + } + + // Extract the CN field + common_name_entry = X509_NAME_get_entry( + X509_get_subject_name((X509 *)server_cert), common_name_loc); + if (common_name_entry == NULL) { + return AMQP_HVR_ERROR; + } + + // Convert the CN field to a C string + common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) { + return AMQP_HVR_ERROR; + } + +#ifdef AMQP_OPENSSL_V110 + common_name_str = (const char *)ASN1_STRING_get0_data(common_name_asn1); +#else + common_name_str = (char *)ASN1_STRING_data(common_name_asn1); +#endif + + // Make sure there isn't an embedded NUL character in the CN + if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { + return AMQP_HVR_MALFORMED_CERTIFICATE; + } + + // Compare expected hostname with the CN + if (amqp_hostcheck(common_name_str, hostname) == AMQP_HCR_MATCH) { + return AMQP_HVR_MATCH_FOUND; + } else { + return AMQP_HVR_MATCH_NOT_FOUND; + } +} + +/** + * Tries to find a match for hostname in the certificate's Subject Alternative + * Name extension. + * + * Returns AMQP_HVR_MATCH_FOUND if a match was found. + * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. + * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL + * character embedded in it. + * Returns AMQP_HVR_NO_SAN_PRESENT if the SAN extension was not present in the + * certificate. + */ +static amqp_hostname_validation_result amqp_matches_subject_alternative_name( + const char *hostname, const X509 *server_cert) { + amqp_hostname_validation_result result = AMQP_HVR_MATCH_NOT_FOUND; + int i; + int san_names_nb = -1; + STACK_OF(GENERAL_NAME) *san_names = NULL; + + // Try to extract the names within the SAN extension from the certificate + san_names = + X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL); + if (san_names == NULL) { + return AMQP_HVR_NO_SAN_PRESENT; + } + san_names_nb = sk_GENERAL_NAME_num(san_names); + + // Check each name within the extension + for (i = 0; i < san_names_nb; i++) { + const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); + + if (current_name->type == GEN_DNS) { + // Current name is a DNS name, let's check it + const char *dns_name = (const char *) +#ifdef AMQP_OPENSSL_V110 + ASN1_STRING_get0_data(current_name->d.dNSName); +#else + ASN1_STRING_data(current_name->d.dNSName); +#endif + + // Make sure there isn't an embedded NUL character in the DNS name + if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != + strlen(dns_name)) { + result = AMQP_HVR_MALFORMED_CERTIFICATE; + break; + } else { // Compare expected hostname with the DNS name + if (amqp_hostcheck(dns_name, hostname) == AMQP_HCR_MATCH) { + result = AMQP_HVR_MATCH_FOUND; + break; + } + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + + return result; +} + +/** + * Validates the server's identity by looking for the expected hostname in the + * server's certificate. As described in RFC 6125, it first tries to find a + * match in the Subject Alternative Name extension. If the extension is not + * present in the certificate, it checks the Common Name instead. + * + * Returns AMQP_HVR_MATCH_FOUND if a match was found. + * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. + * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL + * character embedded in it. + * Returns AMQP_HVR_ERROR if there was an error. + */ +amqp_hostname_validation_result amqp_ssl_validate_hostname( + const char *hostname, const X509 *server_cert) { + amqp_hostname_validation_result result; + + if ((hostname == NULL) || (server_cert == NULL)) return AMQP_HVR_ERROR; + + // First try the Subject Alternative Names extension + result = amqp_matches_subject_alternative_name(hostname, server_cert); + if (result == AMQP_HVR_NO_SAN_PRESENT) { + // Extension was not found: try the Common Name + result = amqp_matches_common_name(hostname, server_cert); + } + + return result; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.h b/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.h new file mode 100644 index 000000000..c99487117 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.h @@ -0,0 +1,58 @@ +#ifndef librabbitmq_amqp_openssl_hostname_validation_h +#define librabbitmq_amqp_openssl_hostname_validation_h + +/* + * Copyright (C) 2012, iSEC Partners. + * Copyright (C) 2015 Alan Antonuk. + * + * All rights reserved. + * + * 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +/* Originally from: + * https://github.com/iSECPartners/ssl-conservatory + * https://wiki.openssl.org/index.php/Hostname_validation + */ + +#include + +typedef enum { + AMQP_HVR_MATCH_FOUND, + AMQP_HVR_MATCH_NOT_FOUND, + AMQP_HVR_NO_SAN_PRESENT, + AMQP_HVR_MALFORMED_CERTIFICATE, + AMQP_HVR_ERROR +} amqp_hostname_validation_result; + +/** +* Validates the server's identity by looking for the expected hostname in the +* server's certificate. As described in RFC 6125, it first tries to find a match +* in the Subject Alternative Name extension. If the extension is not present in +* the certificate, it checks the Common Name instead. +* +* Returns AMQP_HVR_MATCH_FOUND if a match was found. +* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. +* Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL +* character embedded in it. +* Returns AMQP_HVR_ERROR if there was an error. +*/ +amqp_hostname_validation_result amqp_ssl_validate_hostname( + const char *hostname, const X509 *server_cert); + +#endif diff --git a/ext/librabbitmq/librabbitmq/amqp_private.h b/ext/librabbitmq/librabbitmq/amqp_private.h new file mode 100644 index 000000000..e73776c7d --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_private.h @@ -0,0 +1,374 @@ +#ifndef librabbitmq_amqp_private_h +#define librabbitmq_amqp_private_h + +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2014 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define AMQ_COPYRIGHT \ + "Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \ + " and Alan Antonuk." + +#include "amqp.h" +#include "amqp_framing.h" +#include + +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WINVER +/* WINVER 0x0502 is WinXP SP2+, Windows Server 2003 SP1+ + * See: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx#macros_for_conditional_declarations + */ +#define WINVER 0x0502 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#include +#endif + +/* GCC attributes */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define AMQP_NORETURN __attribute__((__noreturn__)) +#define AMQP_UNUSED __attribute__((__unused__)) +#elif defined(_MSC_VER) +#define AMQP_NORETURN __declspec(noreturn) +#define AMQP_UNUSED +#else +#define AMQP_NORETURN +#define AMQP_UNUSED +#endif + +#if __GNUC__ >= 4 +#define AMQP_PRIVATE __attribute__((visibility("hidden"))) +#else +#define AMQP_PRIVATE +#endif + +char *amqp_os_error_string(int err); + +#ifdef WITH_SSL +char *amqp_ssl_error_string(int err); +#endif + +#include "amqp_socket.h" +#include "amqp_time.h" + +/* + * Connection states: XXX FIX THIS + * + * - CONNECTION_STATE_INITIAL: The initial state, when we cannot be + * sure if the next thing we will get is the first AMQP frame, or a + * protocol header from the server. + * + * - CONNECTION_STATE_IDLE: The normal state between + * frames. Connections may only be reconfigured, and the + * connection's pools recycled, when in this state. Whenever we're + * in this state, the inbound_buffer's bytes pointer must be NULL; + * any other state, and it must point to a block of memory allocated + * from the frame_pool. + * + * - CONNECTION_STATE_HEADER: Some bytes of an incoming frame have + * been seen, but not a complete frame header's worth. + * + * - CONNECTION_STATE_BODY: A complete frame header has been seen, but + * the frame is not yet complete. When it is completed, it will be + * returned, and the connection will return to IDLE state. + * + */ +typedef enum amqp_connection_state_enum_ { + CONNECTION_STATE_IDLE = 0, + CONNECTION_STATE_INITIAL, + CONNECTION_STATE_HEADER, + CONNECTION_STATE_BODY +} amqp_connection_state_enum; + +typedef enum amqp_status_private_enum_ { + /* 0x00xx -> AMQP_STATUS_*/ + /* 0x01xx -> AMQP_STATUS_TCP_* */ + /* 0x02xx -> AMQP_STATUS_SSL_* */ + AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD = -0x1301, + AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE = -0x1302 +} amqp_status_private_enum; + +/* 7 bytes up front, then payload, then 1 byte footer */ +#define HEADER_SIZE 7 +#define FOOTER_SIZE 1 + +#define AMQP_PSEUDOFRAME_PROTOCOL_HEADER 'A' + +typedef struct amqp_link_t_ { + struct amqp_link_t_ *next; + void *data; +} amqp_link_t; + +#define POOL_TABLE_SIZE 16 + +typedef struct amqp_pool_table_entry_t_ { + struct amqp_pool_table_entry_t_ *next; + amqp_pool_t pool; + amqp_channel_t channel; +} amqp_pool_table_entry_t; + +struct amqp_connection_state_t_ { + amqp_pool_table_entry_t *pool_table[POOL_TABLE_SIZE]; + + amqp_connection_state_enum state; + + int channel_max; + int frame_max; + + /* Heartbeat interval in seconds. If this is <= 0, then heartbeats are not + * enabled, and next_recv_heartbeat and next_send_heartbeat are set to + * infinite */ + int heartbeat; + amqp_time_t next_recv_heartbeat; + amqp_time_t next_send_heartbeat; + + /* buffer for holding frame headers. Allows us to delay allocating + * the raw frame buffer until the type, channel, and size are all known + */ + char header_buffer[HEADER_SIZE + 1]; + amqp_bytes_t inbound_buffer; + + size_t inbound_offset; + size_t target_size; + + amqp_bytes_t outbound_buffer; + + amqp_socket_t *socket; + + amqp_bytes_t sock_inbound_buffer; + size_t sock_inbound_offset; + size_t sock_inbound_limit; + + amqp_link_t *first_queued_frame; + amqp_link_t *last_queued_frame; + + amqp_rpc_reply_t most_recent_api_result; + + amqp_table_t server_properties; + amqp_table_t client_properties; + amqp_pool_t properties_pool; + + struct timeval *handshake_timeout; + struct timeval internal_handshake_timeout; + struct timeval *rpc_timeout; + struct timeval internal_rpc_timeout; +}; + +amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t connection, + amqp_channel_t channel); +amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state, + amqp_channel_t channel); + +static inline int amqp_heartbeat_send(amqp_connection_state_t state) { + return state->heartbeat; +} + +static inline int amqp_heartbeat_recv(amqp_connection_state_t state) { + return 2 * state->heartbeat; +} + +int amqp_try_recv(amqp_connection_state_t state); + +static inline void *amqp_offset(void *data, size_t offset) { + return (char *)data + offset; +} + +/* This macro defines the encoding and decoding functions associated with a + simple type. */ + +#define DECLARE_CODEC_BASE_TYPE(bits) \ + \ + static inline int amqp_encode_##bits(amqp_bytes_t encoded, size_t *offset, \ + uint##bits##_t input) { \ + size_t o = *offset; \ + if ((*offset = o + bits / 8) <= encoded.len) { \ + amqp_e##bits(input, amqp_offset(encoded.bytes, o)); \ + return 1; \ + } \ + return 0; \ + } \ + \ + static inline int amqp_decode_##bits(amqp_bytes_t encoded, size_t *offset, \ + uint##bits##_t *output) { \ + size_t o = *offset; \ + if ((*offset = o + bits / 8) <= encoded.len) { \ + *output = amqp_d##bits(amqp_offset(encoded.bytes, o)); \ + return 1; \ + } \ + return 0; \ + } + +static inline int is_bigendian(void) { + union { + uint32_t i; + char c[4]; + } bint = {0x01020304}; + return bint.c[0] == 1; +} + +static inline void amqp_e8(uint8_t val, void *data) { + memcpy(data, &val, sizeof(val)); +} + +static inline uint8_t amqp_d8(void *data) { + uint8_t val; + memcpy(&val, data, sizeof(val)); + return val; +} + +static inline void amqp_e16(uint16_t val, void *data) { + if (!is_bigendian()) { + val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u); + } + memcpy(data, &val, sizeof(val)); +} + +static inline uint16_t amqp_d16(void *data) { + uint16_t val; + memcpy(&val, data, sizeof(val)); + if (!is_bigendian()) { + val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u); + } + return val; +} + +static inline void amqp_e32(uint32_t val, void *data) { + if (!is_bigendian()) { + val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) | + ((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u); + } + memcpy(data, &val, sizeof(val)); +} + +static inline uint32_t amqp_d32(void *data) { + uint32_t val; + memcpy(&val, data, sizeof(val)); + if (!is_bigendian()) { + val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) | + ((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u); + } + return val; +} + +static inline void amqp_e64(uint64_t val, void *data) { + if (!is_bigendian()) { + val = ((val & 0xFF00000000000000u) >> 56u) | + ((val & 0x00FF000000000000u) >> 40u) | + ((val & 0x0000FF0000000000u) >> 24u) | + ((val & 0x000000FF00000000u) >> 8u) | + ((val & 0x00000000FF000000u) << 8u) | + ((val & 0x0000000000FF0000u) << 24u) | + ((val & 0x000000000000FF00u) << 40u) | + ((val & 0x00000000000000FFu) << 56u); + } + memcpy(data, &val, sizeof(val)); +} + +static inline uint64_t amqp_d64(void *data) { + uint64_t val; + memcpy(&val, data, sizeof(val)); + if (!is_bigendian()) { + val = ((val & 0xFF00000000000000u) >> 56u) | + ((val & 0x00FF000000000000u) >> 40u) | + ((val & 0x0000FF0000000000u) >> 24u) | + ((val & 0x000000FF00000000u) >> 8u) | + ((val & 0x00000000FF000000u) << 8u) | + ((val & 0x0000000000FF0000u) << 24u) | + ((val & 0x000000000000FF00u) << 40u) | + ((val & 0x00000000000000FFu) << 56u); + } + return val; +} + +DECLARE_CODEC_BASE_TYPE(8) +DECLARE_CODEC_BASE_TYPE(16) +DECLARE_CODEC_BASE_TYPE(32) +DECLARE_CODEC_BASE_TYPE(64) + +static inline int amqp_encode_bytes(amqp_bytes_t encoded, size_t *offset, + amqp_bytes_t input) { + size_t o = *offset; + /* The memcpy below has undefined behavior if the input is NULL. It is valid + * for a 0-length amqp_bytes_t to have .bytes == NULL. Thus we should check + * before encoding. + */ + if (input.len == 0) { + return 1; + } + if ((*offset = o + input.len) <= encoded.len) { + memcpy(amqp_offset(encoded.bytes, o), input.bytes, input.len); + return 1; + } else { + return 0; + } +} + +static inline int amqp_decode_bytes(amqp_bytes_t encoded, size_t *offset, + amqp_bytes_t *output, size_t len) { + size_t o = *offset; + if ((*offset = o + len) <= encoded.len) { + output->bytes = amqp_offset(encoded.bytes, o); + output->len = len; + return 1; + } else { + return 0; + } +} + +AMQP_NORETURN +void amqp_abort(const char *fmt, ...); + +int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l); + +static inline amqp_rpc_reply_t amqp_rpc_reply_error(amqp_status_enum status) { + amqp_rpc_reply_t reply; + reply.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; + reply.library_error = status; + return reply; +} + +int amqp_send_frame_inner(amqp_connection_state_t state, + const amqp_frame_t *frame, int flags, + amqp_time_t deadline); +#endif diff --git a/ext/librabbitmq/librabbitmq/amqp_socket.c b/ext/librabbitmq/librabbitmq/amqp_socket.c new file mode 100644 index 000000000..061192ea9 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_socket.c @@ -0,0 +1,1492 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2014 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "amqp_private.h" +#include "amqp_socket.h" +#include "amqp_table.h" +#include "amqp_time.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#else +#include +/* On older BSD types.h must come before net includes */ +#include +#include +#ifdef HAVE_SELECT +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_POLL +#include +#endif +#include +#endif + +static int amqp_id_in_reply_list(amqp_method_number_t expected, + amqp_method_number_t *list); + +static int amqp_os_socket_init(void) { +#ifdef _WIN32 + static int called_wsastartup = 0; + if (!called_wsastartup) { + WSADATA data; + int res = WSAStartup(0x0202, &data); + if (res) { + return AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR; + } + + called_wsastartup = 1; + } + return AMQP_STATUS_OK; + +#else + return AMQP_STATUS_OK; +#endif +} + +int amqp_os_socket_error(void) { +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +int amqp_os_socket_close(int sockfd) { +#ifdef _WIN32 + return closesocket(sockfd); +#else + return close(sockfd); +#endif +} + +ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len, + int flags) { + assert(self); + assert(self->klass->send); + return self->klass->send(self, buf, len, flags); +} + +ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, + int flags) { + assert(self); + assert(self->klass->recv); + return self->klass->recv(self, buf, len, flags); +} + +int amqp_socket_open(amqp_socket_t *self, const char *host, int port) { + assert(self); + assert(self->klass->open); + return self->klass->open(self, host, port, NULL); +} + +int amqp_socket_open_noblock(amqp_socket_t *self, const char *host, int port, + struct timeval *timeout) { + assert(self); + assert(self->klass->open); + return self->klass->open(self, host, port, timeout); +} + +int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) { + assert(self); + assert(self->klass->close); + return self->klass->close(self, force); +} + +void amqp_socket_delete(amqp_socket_t *self) { + if (self) { + assert(self->klass->delete); + self->klass->delete (self); + } +} + +int amqp_socket_get_sockfd(amqp_socket_t *self) { + assert(self); + assert(self->klass->get_sockfd); + return self->klass->get_sockfd(self); +} + +int amqp_poll(int fd, int event, amqp_time_t deadline) { +#ifdef HAVE_POLL + struct pollfd pfd; + int res; + int timeout_ms; + + /* Function should only ever be called with one of these two */ + assert(event == AMQP_SF_POLLIN || event == AMQP_SF_POLLOUT); + +start_poll: + pfd.fd = fd; + switch (event) { + case AMQP_SF_POLLIN: + pfd.events = POLLIN; + break; + case AMQP_SF_POLLOUT: + pfd.events = POLLOUT; + break; + } + + timeout_ms = amqp_time_ms_until(deadline); + if (-1 > timeout_ms) { + return timeout_ms; + } + + res = poll(&pfd, 1, timeout_ms); + + if (0 < res) { + /* TODO: optimize this a bit by returning the AMQP_STATUS_SOCKET_ERROR or + * equivalent when pdf.revent is POLLHUP or POLLERR, so an extra syscall + * doesn't need to be made. */ + return AMQP_STATUS_OK; + } else if (0 == res) { + return AMQP_STATUS_TIMEOUT; + } else { + switch (amqp_os_socket_error()) { + case EINTR: + goto start_poll; + default: + return AMQP_STATUS_SOCKET_ERROR; + } + } + return AMQP_STATUS_OK; +#elif defined(HAVE_SELECT) + fd_set fds; + fd_set exceptfds; + fd_set *exceptfdsp; + int res; + struct timeval tv; + struct timeval *tvp; + + assert((0 != (event & AMQP_SF_POLLIN)) || (0 != (event & AMQP_SF_POLLOUT))); +#ifndef _WIN32 + /* On Win32 connect() failure is indicated through the exceptfds, it does not + * make any sense to allow POLLERR on any other platform or condition */ + assert(0 == (event & AMQP_SF_POLLERR)); +#endif + +start_select: + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (event & AMQP_SF_POLLERR) { + FD_ZERO(&exceptfds); + FD_SET(fd, &exceptfds); + exceptfdsp = &exceptfds; + } else { + exceptfdsp = NULL; + } + + res = amqp_time_tv_until(deadline, &tv, &tvp); + if (res != AMQP_STATUS_OK) { + return res; + } + + if (event & AMQP_SF_POLLIN) { + res = select(fd + 1, &fds, NULL, exceptfdsp, tvp); + } else if (event & AMQP_SF_POLLOUT) { + res = select(fd + 1, NULL, &fds, exceptfdsp, tvp); + } + + if (0 < res) { + return AMQP_STATUS_OK; + } else if (0 == res) { + return AMQP_STATUS_TIMEOUT; + } else { + switch (amqp_os_socket_error()) { + case EINTR: + goto start_select; + default: + return AMQP_STATUS_SOCKET_ERROR; + } + } +#else +#error "poll() or select() is needed to compile rabbitmq-c" +#endif +} + +static ssize_t do_poll(amqp_connection_state_t state, ssize_t res, + amqp_time_t deadline) { + int fd = amqp_get_sockfd(state); + if (-1 == fd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + switch (res) { + case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD: + res = amqp_poll(fd, AMQP_SF_POLLIN, deadline); + break; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE: + res = amqp_poll(fd, AMQP_SF_POLLOUT, deadline); + break; + } + return res; +} + +ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf, + size_t len, amqp_time_t deadline, int flags) { + ssize_t res; + void *buf_left = (void *)buf; + /* Assume that len is not going to be larger than ssize_t can hold. */ + ssize_t len_left = (size_t)len; + +start_send: + res = amqp_socket_send(state->socket, buf_left, len_left, flags); + + if (res > 0) { + len_left -= res; + buf_left = (char *)buf_left + res; + if (0 == len_left) { + return (ssize_t)len; + } + goto start_send; + } + res = do_poll(state, res, deadline); + if (AMQP_STATUS_OK == res) { + goto start_send; + } + if (AMQP_STATUS_TIMEOUT == res) { + return (ssize_t)len - len_left; + } + return res; +} + +int amqp_open_socket(char const *hostname, int portnumber) { + return amqp_open_socket_inner(hostname, portnumber, amqp_time_infinite()); +} + +int amqp_open_socket_noblock(char const *hostname, int portnumber, + struct timeval *timeout) { + amqp_time_t deadline; + int res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + return res; + } + return amqp_open_socket_inner(hostname, portnumber, deadline); +} + +#ifdef _WIN32 +static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) { + int one = 1; + SOCKET sockfd; + int last_error; + + /* + * This cast is to squash warnings on Win64, see: + * http://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64 + */ + + sockfd = (int)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (INVALID_SOCKET == sockfd) { + return AMQP_STATUS_SOCKET_ERROR; + } + + /* Set the socket to be non-blocking */ + if (SOCKET_ERROR == ioctlsocket(sockfd, FIONBIO, &one)) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Disable nagle */ + if (SOCKET_ERROR == setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, + (const char *)&one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Enable TCP keepalives */ + if (SOCKET_ERROR == setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + if (SOCKET_ERROR != connect(sockfd, addr->ai_addr, (int)addr->ai_addrlen)) { + return (int)sockfd; + } + + if (WSAEWOULDBLOCK != WSAGetLastError()) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + last_error = + amqp_poll((int)sockfd, AMQP_SF_POLLOUT | AMQP_SF_POLLERR, deadline); + if (AMQP_STATUS_OK != last_error) { + goto err; + } + + { + int result; + int result_len = sizeof(result); + + if (SOCKET_ERROR == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, + (char *)&result, &result_len) || + result != 0) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + } + + return (int)sockfd; + +err: + closesocket(sockfd); + return last_error; +} +#else +static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) { + int one = 1; + int sockfd; + int flags; + int last_error; + + sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (-1 == sockfd) { + return AMQP_STATUS_SOCKET_ERROR; + } + + /* Enable CLOEXEC on socket */ + flags = fcntl(sockfd, F_GETFD); + if (flags == -1 || fcntl(sockfd, F_SETFD, (long)(flags | FD_CLOEXEC)) == -1) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Set the socket as non-blocking */ + flags = fcntl(sockfd, F_GETFL); + if (flags == -1 || fcntl(sockfd, F_SETFL, (long)(flags | O_NONBLOCK)) == -1) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + +#ifdef SO_NOSIGPIPE + /* Turn off SIGPIPE on platforms that support it, BSD, MacOSX */ + if (0 != setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } +#endif /* SO_NOSIGPIPE */ + + /* Disable nagle */ + if (0 != setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + /* Enable TCP keepalives */ + if (0 != setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + if (0 == connect(sockfd, addr->ai_addr, addr->ai_addrlen)) { + return sockfd; + } + + if (EINPROGRESS != errno) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + + last_error = amqp_poll(sockfd, AMQP_SF_POLLOUT, deadline); + if (AMQP_STATUS_OK != last_error) { + goto err; + } + + { + int result; + socklen_t result_len = sizeof(result); + + if (-1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &result_len) || + result != 0) { + last_error = AMQP_STATUS_SOCKET_ERROR; + goto err; + } + } + + return sockfd; + +err: + close(sockfd); + return last_error; +} +#endif + +int amqp_open_socket_inner(char const *hostname, int portnumber, + amqp_time_t deadline) { + struct addrinfo hint; + struct addrinfo *address_list; + struct addrinfo *addr; + char portnumber_string[33]; + int sockfd = -1; + int last_error; + + last_error = amqp_os_socket_init(); + if (AMQP_STATUS_OK != last_error) { + return last_error; + } + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */ + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + + (void)sprintf(portnumber_string, "%d", portnumber); + + last_error = getaddrinfo(hostname, portnumber_string, &hint, &address_list); + if (0 != last_error) { + return AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED; + } + + for (addr = address_list; addr; addr = addr->ai_next) { + sockfd = connect_socket(addr, deadline); + + if (sockfd >= 0) { + last_error = AMQP_STATUS_OK; + break; + } else if (sockfd == AMQP_STATUS_TIMEOUT) { + last_error = sockfd; + break; + } + } + + freeaddrinfo(address_list); + if (last_error != AMQP_STATUS_OK || sockfd == -1) { + return last_error; + } + return sockfd; +} + +static int send_header_inner(amqp_connection_state_t state, + amqp_time_t deadline) { + ssize_t res; + static const uint8_t header[8] = {'A', + 'M', + 'Q', + 'P', + 0, + AMQP_PROTOCOL_VERSION_MAJOR, + AMQP_PROTOCOL_VERSION_MINOR, + AMQP_PROTOCOL_VERSION_REVISION}; + res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE); + if (sizeof(header) == res) { + return AMQP_STATUS_OK; + } + return (int)res; +} + +int amqp_send_header(amqp_connection_state_t state) { + return send_header_inner(state, amqp_time_infinite()); +} + +static amqp_bytes_t sasl_method_name(amqp_sasl_method_enum method) { + amqp_bytes_t res; + + switch (method) { + case AMQP_SASL_METHOD_PLAIN: + res = amqp_cstring_bytes("PLAIN"); + break; + case AMQP_SASL_METHOD_EXTERNAL: + res = amqp_cstring_bytes("EXTERNAL"); + break; + + default: + amqp_abort("Invalid SASL method: %d", (int)method); + } + + return res; +} + +static int bytes_equal(amqp_bytes_t l, amqp_bytes_t r) { + if (l.len == r.len) { + if (l.bytes && r.bytes) { + if (0 == memcmp(l.bytes, r.bytes, l.len)) { + return 1; + } + } + } + return 0; +} + +int sasl_mechanism_in_list(amqp_bytes_t mechanisms, + amqp_sasl_method_enum method) { + amqp_bytes_t mechanism; + amqp_bytes_t supported_mechanism; + uint8_t *start; + uint8_t *end; + uint8_t *current; + + assert(NULL != mechanisms.bytes); + + mechanism = sasl_method_name(method); + + start = (uint8_t *)mechanisms.bytes; + current = start; + end = start + mechanisms.len; + + for (; current != end; start = current + 1) { + /* HACK: SASL states that we should be parsing this string as a UTF-8 + * string, which we're plainly not doing here. At this point its not worth + * dragging an entire UTF-8 parser for this one case, and this should work + * most of the time */ + current = memchr(start, ' ', end - start); + if (NULL == current) { + current = end; + } + supported_mechanism.bytes = start; + supported_mechanism.len = current - start; + if (bytes_equal(mechanism, supported_mechanism)) { + return 1; + } + } + + return 0; +} + +static amqp_bytes_t sasl_response(amqp_pool_t *pool, + amqp_sasl_method_enum method, va_list args) { + amqp_bytes_t response; + + switch (method) { + case AMQP_SASL_METHOD_PLAIN: { + char *username = va_arg(args, char *); + size_t username_len = strlen(username); + char *password = va_arg(args, char *); + size_t password_len = strlen(password); + char *response_buf; + + amqp_pool_alloc_bytes(pool, strlen(username) + strlen(password) + 2, + &response); + if (response.bytes == NULL) + /* We never request a zero-length block, because of the +2 + above, so a NULL here really is ENOMEM. */ + { + return response; + } + + response_buf = response.bytes; + response_buf[0] = 0; + memcpy(response_buf + 1, username, username_len); + response_buf[username_len + 1] = 0; + memcpy(response_buf + username_len + 2, password, password_len); + break; + } + case AMQP_SASL_METHOD_EXTERNAL: { + char *identity = va_arg(args, char *); + size_t identity_len = strlen(identity); + + amqp_pool_alloc_bytes(pool, identity_len, &response); + if (response.bytes == NULL) { + return response; + } + + memcpy(response.bytes, identity, identity_len); + break; + } + default: + amqp_abort("Invalid SASL method: %d", (int)method); + } + + return response; +} + +amqp_boolean_t amqp_frames_enqueued(amqp_connection_state_t state) { + return (state->first_queued_frame != NULL); +} + +/* + * Check to see if we have data in our buffer. If this returns 1, we + * will avoid an immediate blocking read in amqp_simple_wait_frame. + */ +amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) { + return (state->sock_inbound_offset < state->sock_inbound_limit); +} + +static int consume_one_frame(amqp_connection_state_t state, + amqp_frame_t *decoded_frame) { + int res; + + amqp_bytes_t buffer; + buffer.len = state->sock_inbound_limit - state->sock_inbound_offset; + buffer.bytes = + ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset; + + res = amqp_handle_input(state, buffer, decoded_frame); + if (res < 0) { + return res; + } + + state->sock_inbound_offset += res; + + return AMQP_STATUS_OK; +} + +static int recv_with_timeout(amqp_connection_state_t state, + amqp_time_t timeout) { + ssize_t res; + int fd; + +start_recv: + res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes, + state->sock_inbound_buffer.len, 0); + + if (res < 0) { + fd = amqp_get_sockfd(state); + if (-1 == fd) { + return AMQP_STATUS_CONNECTION_CLOSED; + } + switch (res) { + default: + return (int)res; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD: + res = amqp_poll(fd, AMQP_SF_POLLIN, timeout); + break; + case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE: + res = amqp_poll(fd, AMQP_SF_POLLOUT, timeout); + break; + } + if (AMQP_STATUS_OK == res) { + goto start_recv; + } + return (int)res; + } + + state->sock_inbound_limit = res; + state->sock_inbound_offset = 0; + + res = amqp_time_s_from_now(&state->next_recv_heartbeat, + amqp_heartbeat_recv(state)); + if (AMQP_STATUS_OK != res) { + return (int)res; + } + return AMQP_STATUS_OK; +} + +int amqp_try_recv(amqp_connection_state_t state) { + amqp_time_t timeout; + + while (amqp_data_in_buffer(state)) { + amqp_frame_t frame; + int res = consume_one_frame(state, &frame); + + if (AMQP_STATUS_OK != res) { + return res; + } + + if (frame.frame_type != 0) { + amqp_pool_t *channel_pool; + amqp_frame_t *frame_copy; + amqp_link_t *link; + + channel_pool = amqp_get_or_create_channel_pool(state, frame.channel); + if (NULL == channel_pool) { + return AMQP_STATUS_NO_MEMORY; + } + + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + + if (frame_copy == NULL || link == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + + *frame_copy = frame; + + link->next = NULL; + link->data = frame_copy; + + if (state->last_queued_frame == NULL) { + state->first_queued_frame = link; + } else { + state->last_queued_frame->next = link; + } + state->last_queued_frame = link; + } + } + timeout = amqp_time_immediate(); + + return recv_with_timeout(state, timeout); +} + +static int wait_frame_inner(amqp_connection_state_t state, + amqp_frame_t *decoded_frame, + amqp_time_t timeout_deadline) { + amqp_time_t deadline; + int res; + + for (;;) { + while (amqp_data_in_buffer(state)) { + res = consume_one_frame(state, decoded_frame); + + if (AMQP_STATUS_OK != res) { + return res; + } + + if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) { + amqp_maybe_release_buffers_on_channel(state, 0); + continue; + } + + if (decoded_frame->frame_type != 0) { + /* Complete frame was read. Return it. */ + return AMQP_STATUS_OK; + } + } + + beginrecv: + res = amqp_time_has_past(state->next_send_heartbeat); + if (AMQP_STATUS_TIMER_FAILURE == res) { + return res; + } else if (AMQP_STATUS_TIMEOUT == res) { + amqp_frame_t heartbeat; + heartbeat.channel = 0; + heartbeat.frame_type = AMQP_FRAME_HEARTBEAT; + + res = amqp_send_frame(state, &heartbeat); + if (AMQP_STATUS_OK != res) { + return res; + } + } + deadline = amqp_time_first(timeout_deadline, + amqp_time_first(state->next_recv_heartbeat, + state->next_send_heartbeat)); + + /* TODO this needs to wait for a _frame_ and not anything written from the + * socket */ + res = recv_with_timeout(state, deadline); + + if (AMQP_STATUS_TIMEOUT == res) { + if (amqp_time_equal(deadline, state->next_recv_heartbeat)) { + amqp_socket_close(state->socket, AMQP_SC_FORCE); + return AMQP_STATUS_HEARTBEAT_TIMEOUT; + } else if (amqp_time_equal(deadline, timeout_deadline)) { + return AMQP_STATUS_TIMEOUT; + } else if (amqp_time_equal(deadline, state->next_send_heartbeat)) { + /* send heartbeat happens before we do recv_with_timeout */ + goto beginrecv; + } else { + amqp_abort("Internal error: unable to determine timeout reason"); + } + } else if (AMQP_STATUS_OK != res) { + return res; + } + } +} + +static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state, + amqp_frame_t *frame) { + amqp_link_t *link; + amqp_frame_t *frame_copy; + + amqp_pool_t *channel_pool = + amqp_get_or_create_channel_pool(state, frame->channel); + + if (NULL == channel_pool) { + return NULL; + } + + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + + if (NULL == link || NULL == frame_copy) { + return NULL; + } + + *frame_copy = *frame; + link->data = frame_copy; + + return link; +} + +int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) { + amqp_link_t *link = amqp_create_link_for_frame(state, frame); + if (NULL == link) { + return AMQP_STATUS_NO_MEMORY; + } + + if (NULL == state->first_queued_frame) { + state->first_queued_frame = link; + } else { + state->last_queued_frame->next = link; + } + + link->next = NULL; + state->last_queued_frame = link; + + return AMQP_STATUS_OK; +} + +int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) { + amqp_link_t *link = amqp_create_link_for_frame(state, frame); + if (NULL == link) { + return AMQP_STATUS_NO_MEMORY; + } + + if (NULL == state->first_queued_frame) { + state->first_queued_frame = link; + state->last_queued_frame = link; + link->next = NULL; + } else { + link->next = state->first_queued_frame; + state->first_queued_frame = link; + } + + return AMQP_STATUS_OK; +} + +int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_frame_t *decoded_frame) { + amqp_frame_t *frame_ptr; + amqp_link_t *cur; + int res; + + for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) { + frame_ptr = cur->data; + + if (channel == frame_ptr->channel) { + state->first_queued_frame = cur->next; + if (NULL == state->first_queued_frame) { + state->last_queued_frame = NULL; + } + + *decoded_frame = *frame_ptr; + + return AMQP_STATUS_OK; + } + } + + for (;;) { + res = wait_frame_inner(state, decoded_frame, amqp_time_infinite()); + + if (AMQP_STATUS_OK != res) { + return res; + } + + if (channel == decoded_frame->channel) { + return AMQP_STATUS_OK; + } else { + res = amqp_queue_frame(state, decoded_frame); + if (res != AMQP_STATUS_OK) { + return res; + } + } + } +} + +int amqp_simple_wait_frame(amqp_connection_state_t state, + amqp_frame_t *decoded_frame) { + return amqp_simple_wait_frame_noblock(state, decoded_frame, NULL); +} + +int amqp_simple_wait_frame_noblock(amqp_connection_state_t state, + amqp_frame_t *decoded_frame, + struct timeval *timeout) { + amqp_time_t deadline; + + int res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + return res; + } + + if (state->first_queued_frame != NULL) { + amqp_frame_t *f = (amqp_frame_t *)state->first_queued_frame->data; + state->first_queued_frame = state->first_queued_frame->next; + if (state->first_queued_frame == NULL) { + state->last_queued_frame = NULL; + } + *decoded_frame = *f; + return AMQP_STATUS_OK; + } else { + return wait_frame_inner(state, decoded_frame, deadline); + } +} + +static int amqp_simple_wait_method_list(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t *expected_methods, + amqp_time_t deadline, + amqp_method_t *output) { + amqp_frame_t frame; + struct timeval tv; + struct timeval *tvp; + + int res = amqp_time_tv_until(deadline, &tv, &tvp); + if (res != AMQP_STATUS_OK) { + return res; + } + + res = amqp_simple_wait_frame_noblock(state, &frame, tvp); + if (AMQP_STATUS_OK != res) { + return res; + } + + if (AMQP_FRAME_METHOD != frame.frame_type || + expected_channel != frame.channel || + !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) { + return AMQP_STATUS_WRONG_METHOD; + } + *output = frame.payload.method; + return AMQP_STATUS_OK; +} + +static int simple_wait_method_inner(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_time_t deadline, + amqp_method_t *output) { + amqp_method_number_t expected_methods[] = {expected_method, 0}; + return amqp_simple_wait_method_list(state, expected_channel, expected_methods, + deadline, output); +} + +int amqp_simple_wait_method(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_method_t *output) { + return simple_wait_method_inner(state, expected_channel, expected_method, + amqp_time_infinite(), output); +} + +int amqp_send_method(amqp_connection_state_t state, amqp_channel_t channel, + amqp_method_number_t id, void *decoded) { + return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE, + amqp_time_infinite()); +} + +int amqp_send_method_inner(amqp_connection_state_t state, + amqp_channel_t channel, amqp_method_number_t id, + void *decoded, int flags, amqp_time_t deadline) { + amqp_frame_t frame; + + frame.frame_type = AMQP_FRAME_METHOD; + frame.channel = channel; + frame.payload.method.id = id; + frame.payload.method.decoded = decoded; + return amqp_send_frame_inner(state, &frame, flags, deadline); +} + +static int amqp_id_in_reply_list(amqp_method_number_t expected, + amqp_method_number_t *list) { + while (*list != 0) { + if (*list == expected) { + return 1; + } + list++; + } + return 0; +} + +static amqp_rpc_reply_t simple_rpc_inner( + amqp_connection_state_t state, amqp_channel_t channel, + amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids, + void *decoded_request_method, amqp_time_t deadline) { + int status; + amqp_rpc_reply_t result; + + memset(&result, 0, sizeof(result)); + + status = amqp_send_method(state, channel, request_id, decoded_request_method); + if (status < 0) { + return amqp_rpc_reply_error(status); + } + + { + amqp_frame_t frame; + + retry: + status = wait_frame_inner(state, &frame, deadline); + if (status < 0) { + if (status == AMQP_STATUS_TIMEOUT) { + amqp_socket_close(state->socket, AMQP_SC_FORCE); + } + return amqp_rpc_reply_error(status); + } + + /* + * We store the frame for later processing unless it's something + * that directly affects us here, namely a method frame that is + * either + * - on the channel we want, and of the expected type, or + * - on the channel we want, and a channel.close frame, or + * - on channel zero, and a connection.close frame. + */ + if (!((frame.frame_type == AMQP_FRAME_METHOD) && + (((frame.channel == channel) && + (amqp_id_in_reply_list(frame.payload.method.id, + expected_reply_ids) || + (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) || + ((frame.channel == 0) && + (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) { + amqp_pool_t *channel_pool; + amqp_frame_t *frame_copy; + amqp_link_t *link; + + channel_pool = amqp_get_or_create_channel_pool(state, frame.channel); + if (NULL == channel_pool) { + return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY); + } + + frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t)); + link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t)); + + if (frame_copy == NULL || link == NULL) { + return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY); + } + + *frame_copy = frame; + + link->next = NULL; + link->data = frame_copy; + + if (state->last_queued_frame == NULL) { + state->first_queued_frame = link; + } else { + state->last_queued_frame->next = link; + } + state->last_queued_frame = link; + + goto retry; + } + + result.reply_type = + (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) + ? AMQP_RESPONSE_NORMAL + : AMQP_RESPONSE_SERVER_EXCEPTION; + + result.reply = frame.payload.method; + return result; + } +} + +amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t *expected_reply_ids, + void *decoded_request_method) { + amqp_time_t deadline; + int res; + + res = amqp_time_from_now(&deadline, state->rpc_timeout); + if (res != AMQP_STATUS_OK) { + return amqp_rpc_reply_error(res); + } + + return simple_rpc_inner(state, channel, request_id, expected_reply_ids, + decoded_request_method, deadline); +} + +void *amqp_simple_rpc_decoded(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t reply_id, + void *decoded_request_method) { + amqp_time_t deadline; + int res; + amqp_method_number_t replies[2]; + + res = amqp_time_from_now(&deadline, state->rpc_timeout); + if (res != AMQP_STATUS_OK) { + state->most_recent_api_result = amqp_rpc_reply_error(res); + return NULL; + } + + replies[0] = reply_id; + replies[1] = 0; + + state->most_recent_api_result = simple_rpc_inner( + state, channel, request_id, replies, decoded_request_method, deadline); + + if (state->most_recent_api_result.reply_type == AMQP_RESPONSE_NORMAL) { + return state->most_recent_api_result.reply.decoded; + } else { + return NULL; + } +} + +amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) { + return state->most_recent_api_result; +} + +/* + * Merge base and add tables. If the two tables contain an entry with the same + * key, the entry from the add table takes precedence. For entries that are both + * tables with the same key, the table is recursively merged. + */ +int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add, + amqp_table_t *result, amqp_pool_t *pool) { + int i; + int res; + amqp_pool_t temp_pool; + amqp_table_t temp_result; + assert(base != NULL); + assert(result != NULL); + assert(pool != NULL); + + if (NULL == add) { + return amqp_table_clone(base, result, pool); + } + + init_amqp_pool(&temp_pool, 4096); + temp_result.num_entries = 0; + temp_result.entries = + amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) * + (base->num_entries + add->num_entries)); + if (NULL == temp_result.entries) { + res = AMQP_STATUS_NO_MEMORY; + goto error_out; + } + for (i = 0; i < base->num_entries; ++i) { + temp_result.entries[temp_result.num_entries] = base->entries[i]; + temp_result.num_entries++; + } + for (i = 0; i < add->num_entries; ++i) { + amqp_table_entry_t *e = + amqp_table_get_entry_by_key(&temp_result, add->entries[i].key); + if (NULL != e) { + if (AMQP_FIELD_KIND_TABLE == add->entries[i].value.kind && + AMQP_FIELD_KIND_TABLE == e->value.kind) { + amqp_table_entry_t *be = + amqp_table_get_entry_by_key(base, add->entries[i].key); + + res = amqp_merge_capabilities(&be->value.value.table, + &add->entries[i].value.value.table, + &e->value.value.table, &temp_pool); + if (AMQP_STATUS_OK != res) { + goto error_out; + } + } else { + e->value = add->entries[i].value; + } + } else { + temp_result.entries[temp_result.num_entries] = add->entries[i]; + temp_result.num_entries++; + } + } + res = amqp_table_clone(&temp_result, result, pool); +error_out: + empty_amqp_pool(&temp_pool); + return res; +} + +static amqp_rpc_reply_t amqp_login_inner( + amqp_connection_state_t state, char const *vhost, int channel_max, + int frame_max, int heartbeat, const amqp_table_t *client_properties, + struct timeval *timeout, amqp_sasl_method_enum sasl_method, va_list vl) { + int res; + amqp_method_t method; + + uint16_t client_channel_max; + uint32_t client_frame_max; + uint16_t client_heartbeat; + + uint16_t server_channel_max; + uint32_t server_frame_max; + uint16_t server_heartbeat; + + amqp_rpc_reply_t result; + amqp_time_t deadline; + + if (channel_max < 0 || channel_max > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + client_channel_max = (uint16_t)channel_max; + + if (frame_max < 0) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + client_frame_max = (uint32_t)frame_max; + + if (heartbeat < 0 || heartbeat > UINT16_MAX) { + return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER); + } + client_heartbeat = (uint16_t)heartbeat; + + res = amqp_time_from_now(&deadline, timeout); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + res = send_header_inner(state, deadline); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD, + deadline, &method); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + { + amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded; + if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) || + (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) { + res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION; + goto error_res; + } + + res = amqp_table_clone(&s->server_properties, &state->server_properties, + &state->properties_pool); + + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + /* TODO: check that our chosen SASL mechanism is in the list of + acceptable mechanisms. Or even let the application choose from + the list! */ + if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) { + res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD; + goto error_res; + } + } + + { + amqp_table_entry_t default_properties[6]; + amqp_table_t default_table; + amqp_table_entry_t client_capabilities[2]; + amqp_table_t client_capabilities_table; + amqp_connection_start_ok_t s; + amqp_pool_t *channel_pool; + amqp_bytes_t response_bytes; + + channel_pool = amqp_get_or_create_channel_pool(state, 0); + if (NULL == channel_pool) { + res = AMQP_STATUS_NO_MEMORY; + goto error_res; + } + + response_bytes = sasl_response(channel_pool, sasl_method, vl); + if (response_bytes.bytes == NULL) { + res = AMQP_STATUS_NO_MEMORY; + goto error_res; + } + + client_capabilities[0] = + amqp_table_construct_bool_entry("authentication_failure_close", 1); + client_capabilities[1] = + amqp_table_construct_bool_entry("exchange_exchange_bindings", 1); + + client_capabilities_table.entries = client_capabilities; + client_capabilities_table.num_entries = + sizeof(client_capabilities) / sizeof(amqp_table_entry_t); + + default_properties[0] = + amqp_table_construct_utf8_entry("product", "rabbitmq-c"); + default_properties[1] = + amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING); + default_properties[2] = + amqp_table_construct_utf8_entry("platform", AMQ_PLATFORM); + default_properties[3] = + amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT); + default_properties[4] = amqp_table_construct_utf8_entry( + "information", "See https://github.com/alanxz/rabbitmq-c"); + default_properties[5] = amqp_table_construct_table_entry( + "capabilities", &client_capabilities_table); + + default_table.entries = default_properties; + default_table.num_entries = + sizeof(default_properties) / sizeof(amqp_table_entry_t); + + res = amqp_merge_capabilities(&default_table, client_properties, + &state->client_properties, channel_pool); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + + s.client_properties = state->client_properties; + s.mechanism = sasl_method_name(sasl_method); + s.response = response_bytes; + s.locale = amqp_cstring_bytes("en_US"); + + res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s, + AMQP_SF_NONE, deadline); + if (res < 0) { + goto error_res; + } + } + + amqp_release_buffers(state); + + { + amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD, + AMQP_CONNECTION_CLOSE_METHOD, 0}; + + res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method); + if (AMQP_STATUS_OK != res) { + goto error_res; + } + } + + if (AMQP_CONNECTION_CLOSE_METHOD == method.id) { + result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; + result.reply = method; + result.library_error = 0; + goto out; + } + + { + amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded; + server_channel_max = s->channel_max; + server_frame_max = s->frame_max; + server_heartbeat = s->heartbeat; + } + + if (server_channel_max != 0 && + (server_channel_max < client_channel_max || client_channel_max == 0)) { + client_channel_max = server_channel_max; + } else if (server_channel_max == 0 && client_channel_max == 0) { + client_channel_max = UINT16_MAX; + } + + if (server_frame_max != 0 && server_frame_max < client_frame_max) { + client_frame_max = server_frame_max; + } + + if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) { + client_heartbeat = server_heartbeat; + } + + res = amqp_tune_connection(state, client_channel_max, client_frame_max, + client_heartbeat); + if (res < 0) { + goto error_res; + } + + { + amqp_connection_tune_ok_t s; + s.frame_max = client_frame_max; + s.channel_max = client_channel_max; + s.heartbeat = client_heartbeat; + + res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s, + AMQP_SF_NONE, deadline); + if (res < 0) { + goto error_res; + } + } + + amqp_release_buffers(state); + + { + amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0}; + amqp_connection_open_t s; + s.virtual_host = amqp_cstring_bytes(vhost); + s.capabilities = amqp_empty_bytes; + s.insist = 1; + + result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies, + &s, deadline); + if (result.reply_type != AMQP_RESPONSE_NORMAL) { + goto out; + } + } + + result.reply_type = AMQP_RESPONSE_NORMAL; + result.reply.id = 0; + result.reply.decoded = NULL; + result.library_error = 0; + amqp_maybe_release_buffers(state); + +out: + return result; + +error_res: + amqp_socket_close(state->socket, AMQP_SC_FORCE); + result = amqp_rpc_reply_error(res); + + goto out; +} + +amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost, + int channel_max, int frame_max, int heartbeat, + int sasl_method, ...) { + va_list vl; + amqp_rpc_reply_t ret; + + va_start(vl, sasl_method); + + ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat, + &amqp_empty_table, state->handshake_timeout, + sasl_method, vl); + + va_end(vl); + + return ret; +} + +amqp_rpc_reply_t amqp_login_with_properties( + amqp_connection_state_t state, char const *vhost, int channel_max, + int frame_max, int heartbeat, const amqp_table_t *client_properties, + int sasl_method, ...) { + va_list vl; + amqp_rpc_reply_t ret; + + va_start(vl, sasl_method); + + ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat, + client_properties, state->handshake_timeout, + sasl_method, vl); + + va_end(vl); + + return ret; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_socket.h b/ext/librabbitmq/librabbitmq/amqp_socket.h new file mode 100644 index 000000000..3101cf605 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_socket.h @@ -0,0 +1,188 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk. + * All Rights Reserved. + * + * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael + * Steinert. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * An abstract socket interface. + */ + +#ifndef AMQP_SOCKET_H +#define AMQP_SOCKET_H + +#include "amqp_private.h" +#include "amqp_time.h" + +AMQP_BEGIN_DECLS + +typedef enum { + AMQP_SF_NONE = 0, + AMQP_SF_MORE = 1, + AMQP_SF_POLLIN = 2, + AMQP_SF_POLLOUT = 4, + AMQP_SF_POLLERR = 8 +} amqp_socket_flag_enum; + +typedef enum { AMQP_SC_NONE = 0, AMQP_SC_FORCE = 1 } amqp_socket_close_enum; + +int amqp_os_socket_error(void); + +int amqp_os_socket_close(int sockfd); + +/* Socket callbacks. */ +typedef ssize_t (*amqp_socket_send_fn)(void *, const void *, size_t, int); +typedef ssize_t (*amqp_socket_recv_fn)(void *, void *, size_t, int); +typedef int (*amqp_socket_open_fn)(void *, const char *, int, struct timeval *); +typedef int (*amqp_socket_close_fn)(void *, amqp_socket_close_enum); +typedef int (*amqp_socket_get_sockfd_fn)(void *); +typedef void (*amqp_socket_delete_fn)(void *); + +/** V-table for amqp_socket_t */ +struct amqp_socket_class_t { + amqp_socket_send_fn send; + amqp_socket_recv_fn recv; + amqp_socket_open_fn open; + amqp_socket_close_fn close; + amqp_socket_get_sockfd_fn get_sockfd; + amqp_socket_delete_fn delete; +}; + +/** Abstract base class for amqp_socket_t */ +struct amqp_socket_t_ { + const struct amqp_socket_class_t *klass; +}; + +/** + * Set set the socket object for a connection + * + * This assigns a socket object to the connection, closing and deleting any + * existing socket + * + * \param [in] state The connection object to add the socket to + * \param [in] socket The socket object to assign to the connection + */ +void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket); + +/** + * Send a message from a socket. + * + * This function wraps send(2) functionality. + * + * This function will only return on error, or when all of the bytes in buf + * have been sent, or when an error occurs. + * + * \param [in,out] self A socket object. + * \param [in] buf A buffer to read from. + * \param [in] len The number of bytes in \e buf. + * \param [in] + * + * \return AMQP_STATUS_OK on success. amqp_status_enum value otherwise + */ +ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len, + int flags); + +ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf, + size_t len, amqp_time_t deadline, int flags); + +/** + * Receive a message from a socket. + * + * This function wraps recv(2) functionality. + * + * \param [in,out] self A socket object. + * \param [out] buf A buffer to write to. + * \param [in] len The number of bytes at \e buf. + * \param [in] flags Receive flags, implementation specific. + * + * \return The number of bytes received, or < 0 on error (\ref amqp_status_enum) + */ +ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, int flags); + +/** + * Close a socket connection and free resources. + * + * This function closes a socket connection and releases any resources used by + * the object. After calling this function the specified socket should no + * longer be referenced. + * + * \param [in,out] self A socket object. + * \param [in] force, if set, just close the socket, don't attempt a TLS + * shutdown. + * + * \return Zero upon success, non-zero otherwise. + */ +int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force); + +/** + * Destroy a socket object + * + * \param [in] self the socket object to delete + */ +void amqp_socket_delete(amqp_socket_t *self); + +/** + * Open a socket connection. + * + * This function opens a socket connection returned from amqp_tcp_socket_new() + * or amqp_ssl_socket_new(). This function should be called after setting + * socket options and prior to assigning the socket to an AMQP connection with + * amqp_set_socket(). + * + * \param [in] host Connect to this host. + * \param [in] port Connect on this remote port. + * \param [in] timeout Max allowed time to spent on opening. If NULL - run in + * blocking mode + * + * \return File descriptor upon success, non-zero negative error code otherwise. + */ +int amqp_open_socket_noblock(char const *hostname, int portnumber, + struct timeval *timeout); + +int amqp_open_socket_inner(char const *hostname, int portnumber, + amqp_time_t deadline); + +/* Wait up to dealline for fd to become readable or writeable depending on + * event (AMQP_SF_POLLIN, AMQP_SF_POLLOUT) */ +int amqp_poll(int fd, int event, amqp_time_t deadline); + +int amqp_send_method_inner(amqp_connection_state_t state, + amqp_channel_t channel, amqp_method_number_t id, + void *decoded, int flags, amqp_time_t deadline); + +int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame); + +int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame); + +int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_frame_t *decoded_frame); + +int sasl_mechanism_in_list(amqp_bytes_t mechanisms, + amqp_sasl_method_enum method); + +int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add, + amqp_table_t *result, amqp_pool_t *pool); +AMQP_END_DECLS + +#endif /* AMQP_SOCKET_H */ diff --git a/ext/librabbitmq/librabbitmq/amqp_ssl_socket.h b/ext/librabbitmq/librabbitmq/amqp_ssl_socket.h new file mode 100644 index 000000000..9977ae4ba --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_ssl_socket.h @@ -0,0 +1,239 @@ +/** \file */ +/* + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk. + * All Rights Reserved. + * + * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael + * Steinert. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AMQP_SSL_H +#define AMQP_SSL_H + +#include + +AMQP_BEGIN_DECLS + +/** + * Create a new SSL/TLS socket object. + * + * The returned socket object is owned by the \ref amqp_connection_state_t + * object and will be destroyed when the state object is destroyed or a new + * socket object is created. + * + * If the socket object creation fails, the \ref amqp_connection_state_t object + * will not be changed. + * + * The object returned by this function can be retrieved from the + * amqp_connection_state_t object later using the amqp_get_socket() function. + * + * Calling this function may result in the underlying SSL library being + * initialized. + * \sa amqp_set_initialize_ssl_library() + * + * \param [in,out] state The connection object that owns the SSL/TLS socket + * \return A new socket object or NULL if an error occurred. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_socket_t *AMQP_CALL amqp_ssl_socket_new(amqp_connection_state_t state); + +/** + * Set the CA certificate. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cacert Path to the CA cert file in PEM format. + * + * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on + * failure. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_ssl_socket_set_cacert(amqp_socket_t *self, + const char *cacert); + +/** + * Set the client key. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cert Path to the client certificate in PEM foramt. + * \param [in] key Path to the client key in PEM format. + * + * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on + * failure. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_ssl_socket_set_key(amqp_socket_t *self, const char *cert, + const char *key); + +/** + * Set the client key from a buffer. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cert Path to the client certificate in PEM foramt. + * \param [in] key A buffer containing client key in PEM format. + * \param [in] n The length of the buffer. + * + * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on + * failure. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_ssl_socket_set_key_buffer(amqp_socket_t *self, + const char *cert, const void *key, + size_t n); + +/** + * Enable or disable peer verification. + * + * \deprecated use \amqp_ssl_socket_set_verify_peer and + * \amqp_ssl_socket_set_verify_hostname instead. + * + * If peer verification is enabled then the common name in the server + * certificate must match the server name. Peer verification is enabled by + * default. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] verify Enable or disable peer verification. + * + * \since v0.4.0 + */ +AMQP_DEPRECATED(AMQP_PUBLIC_FUNCTION void AMQP_CALL amqp_ssl_socket_set_verify( + amqp_socket_t *self, amqp_boolean_t verify)); + +/** + * Enable or disable peer verification. + * + * Peer verification validates the certificate chain that is sent by the broker. + * Hostname validation is controlled by \amqp_ssl_socket_set_verify_peer. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] verify enable or disable peer validation + * + * \since v0.8.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_ssl_socket_set_verify_peer(amqp_socket_t *self, + amqp_boolean_t verify); + +/** + * Enable or disable hostname verification. + * + * Hostname verification checks the broker cert for a CN or SAN that matches the + * hostname that amqp_socket_open() is presented. Peer verification is + * controlled by \amqp_ssl_socket_set_verify_peer + * + * \since v0.8.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_ssl_socket_set_verify_hostname(amqp_socket_t *self, + amqp_boolean_t verify); + +typedef enum { + AMQP_TLSv1 = 1, + AMQP_TLSv1_1 = 2, + AMQP_TLSv1_2 = 3, + AMQP_TLSvLATEST = 0xFFFF +} amqp_tls_version_t; + +/** + * Set min and max TLS versions. + * + * Set the oldest and newest acceptable TLS versions that are acceptable when + * connecting to the broker. Set min == max to restrict to just that + * version. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] min the minimum acceptable TLS version + * \param [in] max the maxmium acceptable TLS version + * \returns AMQP_STATUS_OK on success, AMQP_STATUS_UNSUPPORTED if OpenSSL does + * not support the requested TLS version, AMQP_STATUS_INVALID_PARAMETER if an + * invalid combination of parameters is passed. + * + * \since v0.8.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self, + amqp_tls_version_t min, + amqp_tls_version_t max); + +/** + * Sets whether rabbitmq-c will initialize OpenSSL. + * + * OpenSSL requires a one-time initialization across a whole program, this sets + * whether or not rabbitmq-c will initialize the SSL library when the first call + * to amqp_ssl_socket_new() is made. You should call this function with + * do_init = 0 if the underlying SSL library is initialized somewhere else + * the program. + * + * Failing to initialize or double initialization of the SSL library will + * result in undefined behavior + * + * By default rabbitmq-c will initialize the underlying SSL library. + * + * NOTE: calling this function after the first socket has been opened with + * amqp_open_socket() will not have any effect. + * + * \param [in] do_initialize If 0 rabbitmq-c will not initialize the SSL + * library, otherwise rabbitmq-c will initialize the + * SSL library + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void AMQP_CALL amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize); + +/** + * Initialize the underlying SSL/TLS library. + * + * The OpenSSL library requires a one-time initialization across the whole + * program. + * + * This function unconditionally initializes OpenSSL so that rabbitmq-c may + * use it. + * + * This function is thread-safe, and may be called more than once. + * + * \return AMQP_STATUS_OK on success. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_initialize_ssl_library(void); + +/** + * Uninitialize the underlying SSL/TLS library. + * + * \return AMQP_STATUS_OK on success. + * + * \since v0.9.0 + */ +AMQP_PUBLIC_FUNCTION +int AMQP_CALL amqp_uninitialize_ssl_library(void); + +AMQP_END_DECLS + +#endif /* AMQP_SSL_H */ diff --git a/ext/librabbitmq/librabbitmq/amqp_table.c b/ext/librabbitmq/librabbitmq/amqp_table.c new file mode 100644 index 000000000..24b087cc5 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_table.c @@ -0,0 +1,668 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_private.h" +#include "amqp_table.h" +#include +#include +#include +#include +#include + +#define INITIAL_ARRAY_SIZE 16 +#define INITIAL_TABLE_SIZE 16 + +static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_field_value_t *entry, size_t *offset); + +static int amqp_encode_field_value(amqp_bytes_t encoded, + amqp_field_value_t *entry, size_t *offset); + +/*---------------------------------------------------------------------------*/ + +static int amqp_decode_array(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_array_t *output, size_t *offset) { + uint32_t arraysize; + int num_entries = 0; + int allocated_entries = INITIAL_ARRAY_SIZE; + amqp_field_value_t *entries; + size_t limit; + int res; + + if (!amqp_decode_32(encoded, offset, &arraysize)) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + if (arraysize + *offset > encoded.len) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + entries = malloc(allocated_entries * sizeof(amqp_field_value_t)); + if (entries == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + + limit = *offset + arraysize; + while (*offset < limit) { + if (num_entries >= allocated_entries) { + void *newentries; + allocated_entries = allocated_entries * 2; + newentries = + realloc(entries, allocated_entries * sizeof(amqp_field_value_t)); + res = AMQP_STATUS_NO_MEMORY; + if (newentries == NULL) { + goto out; + } + + entries = newentries; + } + + res = amqp_decode_field_value(encoded, pool, &entries[num_entries], offset); + if (res < 0) { + goto out; + } + + num_entries++; + } + + output->num_entries = num_entries; + output->entries = + amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t)); + /* NULL is legitimate if we requested a zero-length block. */ + if (output->entries == NULL) { + if (num_entries == 0) { + res = AMQP_STATUS_OK; + } else { + res = AMQP_STATUS_NO_MEMORY; + } + goto out; + } + + memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t)); + res = AMQP_STATUS_OK; + +out: + free(entries); + return res; +} + +int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_table_t *output, size_t *offset) { + uint32_t tablesize; + int num_entries = 0; + amqp_table_entry_t *entries; + int allocated_entries = INITIAL_TABLE_SIZE; + size_t limit; + int res; + + if (!amqp_decode_32(encoded, offset, &tablesize)) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + if (tablesize + *offset > encoded.len) { + return AMQP_STATUS_BAD_AMQP_DATA; + } + + entries = malloc(allocated_entries * sizeof(amqp_table_entry_t)); + if (entries == NULL) { + return AMQP_STATUS_NO_MEMORY; + } + + limit = *offset + tablesize; + while (*offset < limit) { + uint8_t keylen; + + res = AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_8(encoded, offset, &keylen)) { + goto out; + } + + if (num_entries >= allocated_entries) { + void *newentries; + allocated_entries = allocated_entries * 2; + newentries = + realloc(entries, allocated_entries * sizeof(amqp_table_entry_t)); + res = AMQP_STATUS_NO_MEMORY; + if (newentries == NULL) { + goto out; + } + + entries = newentries; + } + + res = AMQP_STATUS_BAD_AMQP_DATA; + if (!amqp_decode_bytes(encoded, offset, &entries[num_entries].key, + keylen)) { + goto out; + } + + res = amqp_decode_field_value(encoded, pool, &entries[num_entries].value, + offset); + if (res < 0) { + goto out; + } + + num_entries++; + } + + output->num_entries = num_entries; + output->entries = + amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t)); + /* NULL is legitimate if we requested a zero-length block. */ + if (output->entries == NULL) { + if (num_entries == 0) { + res = AMQP_STATUS_OK; + } else { + res = AMQP_STATUS_NO_MEMORY; + } + goto out; + } + + memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t)); + res = AMQP_STATUS_OK; + +out: + free(entries); + return res; +} + +static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_field_value_t *entry, size_t *offset) { + int res = AMQP_STATUS_BAD_AMQP_DATA; + + if (!amqp_decode_8(encoded, offset, &entry->kind)) { + goto out; + } + +#define TRIVIAL_FIELD_DECODER(bits) \ + if (!amqp_decode_##bits(encoded, offset, &entry->value.u##bits)) goto out; \ + break +#define SIMPLE_FIELD_DECODER(bits, dest, how) \ + { \ + uint##bits##_t val; \ + if (!amqp_decode_##bits(encoded, offset, &val)) goto out; \ + entry->value.dest = how; \ + } \ + break + + switch (entry->kind) { + case AMQP_FIELD_KIND_BOOLEAN: + SIMPLE_FIELD_DECODER(8, boolean, val ? 1 : 0); + + case AMQP_FIELD_KIND_I8: + SIMPLE_FIELD_DECODER(8, i8, (int8_t)val); + case AMQP_FIELD_KIND_U8: + TRIVIAL_FIELD_DECODER(8); + + case AMQP_FIELD_KIND_I16: + SIMPLE_FIELD_DECODER(16, i16, (int16_t)val); + case AMQP_FIELD_KIND_U16: + TRIVIAL_FIELD_DECODER(16); + + case AMQP_FIELD_KIND_I32: + SIMPLE_FIELD_DECODER(32, i32, (int32_t)val); + case AMQP_FIELD_KIND_U32: + TRIVIAL_FIELD_DECODER(32); + + case AMQP_FIELD_KIND_I64: + SIMPLE_FIELD_DECODER(64, i64, (int64_t)val); + case AMQP_FIELD_KIND_U64: + TRIVIAL_FIELD_DECODER(64); + + case AMQP_FIELD_KIND_F32: + TRIVIAL_FIELD_DECODER(32); + /* and by punning, f32 magically gets the right value...! */ + + case AMQP_FIELD_KIND_F64: + TRIVIAL_FIELD_DECODER(64); + /* and by punning, f64 magically gets the right value...! */ + + case AMQP_FIELD_KIND_DECIMAL: + if (!amqp_decode_8(encoded, offset, &entry->value.decimal.decimals) || + !amqp_decode_32(encoded, offset, &entry->value.decimal.value)) { + goto out; + } + break; + + case AMQP_FIELD_KIND_UTF8: + /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the + same implementation, but different interpretations. */ + /* fall through */ + case AMQP_FIELD_KIND_BYTES: { + uint32_t len; + if (!amqp_decode_32(encoded, offset, &len) || + !amqp_decode_bytes(encoded, offset, &entry->value.bytes, len)) { + goto out; + } + break; + } + + case AMQP_FIELD_KIND_ARRAY: + res = amqp_decode_array(encoded, pool, &(entry->value.array), offset); + goto out; + + case AMQP_FIELD_KIND_TIMESTAMP: + TRIVIAL_FIELD_DECODER(64); + + case AMQP_FIELD_KIND_TABLE: + res = amqp_decode_table(encoded, pool, &(entry->value.table), offset); + goto out; + + case AMQP_FIELD_KIND_VOID: + break; + + default: + goto out; + } + + res = AMQP_STATUS_OK; + +out: + return res; +} + +/*---------------------------------------------------------------------------*/ + +static int amqp_encode_array(amqp_bytes_t encoded, amqp_array_t *input, + size_t *offset) { + size_t start = *offset; + int i, res; + + *offset += 4; /* size of the array gets filled in later on */ + + for (i = 0; i < input->num_entries; i++) { + res = amqp_encode_field_value(encoded, &input->entries[i], offset); + if (res < 0) { + goto out; + } + } + + if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + + res = AMQP_STATUS_OK; + +out: + return res; +} + +int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input, + size_t *offset) { + size_t start = *offset; + int i, res; + + *offset += 4; /* size of the table gets filled in later on */ + + for (i = 0; i < input->num_entries; i++) { + if (!amqp_encode_8(encoded, offset, (uint8_t)input->entries[i].key.len)) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + + if (!amqp_encode_bytes(encoded, offset, input->entries[i].key)) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + + res = amqp_encode_field_value(encoded, &input->entries[i].value, offset); + if (res < 0) { + goto out; + } + } + + if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + + res = AMQP_STATUS_OK; + +out: + return res; +} + +static int amqp_encode_field_value(amqp_bytes_t encoded, + amqp_field_value_t *entry, size_t *offset) { + int res = AMQP_STATUS_BAD_AMQP_DATA; + + if (!amqp_encode_8(encoded, offset, entry->kind)) { + goto out; + } + +#define FIELD_ENCODER(bits, val) \ + if (!amqp_encode_##bits(encoded, offset, val)) { \ + res = AMQP_STATUS_TABLE_TOO_BIG; \ + goto out; \ + } \ + break + + switch (entry->kind) { + case AMQP_FIELD_KIND_BOOLEAN: + FIELD_ENCODER(8, entry->value.boolean ? 1 : 0); + + case AMQP_FIELD_KIND_I8: + FIELD_ENCODER(8, entry->value.i8); + case AMQP_FIELD_KIND_U8: + FIELD_ENCODER(8, entry->value.u8); + + case AMQP_FIELD_KIND_I16: + FIELD_ENCODER(16, entry->value.i16); + case AMQP_FIELD_KIND_U16: + FIELD_ENCODER(16, entry->value.u16); + + case AMQP_FIELD_KIND_I32: + FIELD_ENCODER(32, entry->value.i32); + case AMQP_FIELD_KIND_U32: + FIELD_ENCODER(32, entry->value.u32); + + case AMQP_FIELD_KIND_I64: + FIELD_ENCODER(64, entry->value.i64); + case AMQP_FIELD_KIND_U64: + FIELD_ENCODER(64, entry->value.u64); + + case AMQP_FIELD_KIND_F32: + /* by punning, u32 magically gets the right value...! */ + FIELD_ENCODER(32, entry->value.u32); + + case AMQP_FIELD_KIND_F64: + /* by punning, u64 magically gets the right value...! */ + FIELD_ENCODER(64, entry->value.u64); + + case AMQP_FIELD_KIND_DECIMAL: + if (!amqp_encode_8(encoded, offset, entry->value.decimal.decimals) || + !amqp_encode_32(encoded, offset, entry->value.decimal.value)) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + break; + + case AMQP_FIELD_KIND_UTF8: + /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the + same implementation, but different interpretations. */ + /* fall through */ + case AMQP_FIELD_KIND_BYTES: + if (!amqp_encode_32(encoded, offset, (uint32_t)entry->value.bytes.len) || + !amqp_encode_bytes(encoded, offset, entry->value.bytes)) { + res = AMQP_STATUS_TABLE_TOO_BIG; + goto out; + } + break; + + case AMQP_FIELD_KIND_ARRAY: + res = amqp_encode_array(encoded, &entry->value.array, offset); + goto out; + + case AMQP_FIELD_KIND_TIMESTAMP: + FIELD_ENCODER(64, entry->value.u64); + + case AMQP_FIELD_KIND_TABLE: + res = amqp_encode_table(encoded, &entry->value.table, offset); + goto out; + + case AMQP_FIELD_KIND_VOID: + break; + + default: + res = AMQP_STATUS_INVALID_PARAMETER; + goto out; + } + + res = AMQP_STATUS_OK; + +out: + return res; +} + +/*---------------------------------------------------------------------------*/ + +int amqp_table_entry_cmp(void const *entry1, void const *entry2) { + amqp_table_entry_t const *p1 = (amqp_table_entry_t const *)entry1; + amqp_table_entry_t const *p2 = (amqp_table_entry_t const *)entry2; + + int d; + size_t minlen; + + minlen = p1->key.len; + if (p2->key.len < minlen) { + minlen = p2->key.len; + } + + d = memcmp(p1->key.bytes, p2->key.bytes, minlen); + if (d != 0) { + return d; + } + + return (int)p1->key.len - (int)p2->key.len; +} + +static int amqp_field_value_clone(const amqp_field_value_t *original, + amqp_field_value_t *clone, + amqp_pool_t *pool) { + int i; + int res; + clone->kind = original->kind; + + switch (clone->kind) { + case AMQP_FIELD_KIND_BOOLEAN: + clone->value.boolean = original->value.boolean; + break; + + case AMQP_FIELD_KIND_I8: + clone->value.i8 = original->value.i8; + break; + + case AMQP_FIELD_KIND_U8: + clone->value.u8 = original->value.u8; + break; + + case AMQP_FIELD_KIND_I16: + clone->value.i16 = original->value.i16; + break; + + case AMQP_FIELD_KIND_U16: + clone->value.u16 = original->value.u16; + break; + + case AMQP_FIELD_KIND_I32: + clone->value.i32 = original->value.i32; + break; + + case AMQP_FIELD_KIND_U32: + clone->value.u32 = original->value.u32; + break; + + case AMQP_FIELD_KIND_I64: + clone->value.i64 = original->value.i64; + break; + + case AMQP_FIELD_KIND_U64: + case AMQP_FIELD_KIND_TIMESTAMP: + clone->value.u64 = original->value.u64; + break; + + case AMQP_FIELD_KIND_F32: + clone->value.f32 = original->value.f32; + break; + + case AMQP_FIELD_KIND_F64: + clone->value.f64 = original->value.f64; + break; + + case AMQP_FIELD_KIND_DECIMAL: + clone->value.decimal = original->value.decimal; + break; + + case AMQP_FIELD_KIND_UTF8: + case AMQP_FIELD_KIND_BYTES: + if (0 == original->value.bytes.len) { + clone->value.bytes = amqp_empty_bytes; + } else { + amqp_pool_alloc_bytes(pool, original->value.bytes.len, + &clone->value.bytes); + if (NULL == clone->value.bytes.bytes) { + return AMQP_STATUS_NO_MEMORY; + } + memcpy(clone->value.bytes.bytes, original->value.bytes.bytes, + clone->value.bytes.len); + } + break; + + case AMQP_FIELD_KIND_ARRAY: + if (0 == original->value.array.entries) { + clone->value.array = amqp_empty_array; + } else { + clone->value.array.num_entries = original->value.array.num_entries; + clone->value.array.entries = amqp_pool_alloc( + pool, clone->value.array.num_entries * sizeof(amqp_field_value_t)); + if (NULL == clone->value.array.entries) { + return AMQP_STATUS_NO_MEMORY; + } + + for (i = 0; i < clone->value.array.num_entries; ++i) { + res = amqp_field_value_clone(&original->value.array.entries[i], + &clone->value.array.entries[i], pool); + if (AMQP_STATUS_OK != res) { + return res; + } + } + } + break; + + case AMQP_FIELD_KIND_TABLE: + return amqp_table_clone(&original->value.table, &clone->value.table, + pool); + + case AMQP_FIELD_KIND_VOID: + break; + + default: + return AMQP_STATUS_INVALID_PARAMETER; + } + + return AMQP_STATUS_OK; +} + +static int amqp_table_entry_clone(const amqp_table_entry_t *original, + amqp_table_entry_t *clone, + amqp_pool_t *pool) { + if (0 == original->key.len) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + amqp_pool_alloc_bytes(pool, original->key.len, &clone->key); + if (NULL == clone->key.bytes) { + return AMQP_STATUS_NO_MEMORY; + } + + memcpy(clone->key.bytes, original->key.bytes, clone->key.len); + + return amqp_field_value_clone(&original->value, &clone->value, pool); +} + +int amqp_table_clone(const amqp_table_t *original, amqp_table_t *clone, + amqp_pool_t *pool) { + int i; + int res; + clone->num_entries = original->num_entries; + if (0 == clone->num_entries) { + *clone = amqp_empty_table; + return AMQP_STATUS_OK; + } + + clone->entries = + amqp_pool_alloc(pool, clone->num_entries * sizeof(amqp_table_entry_t)); + + if (NULL == clone->entries) { + return AMQP_STATUS_NO_MEMORY; + } + + for (i = 0; i < clone->num_entries; ++i) { + res = + amqp_table_entry_clone(&original->entries[i], &clone->entries[i], pool); + if (AMQP_STATUS_OK != res) { + goto error_out1; + } + } + + return AMQP_STATUS_OK; + +error_out1: + return res; +} + +amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key, + const char *value) { + amqp_table_entry_t ret; + ret.key = amqp_cstring_bytes(key); + ret.value.kind = AMQP_FIELD_KIND_UTF8; + ret.value.value.bytes = amqp_cstring_bytes(value); + return ret; +} + +amqp_table_entry_t amqp_table_construct_table_entry(const char *key, + const amqp_table_t *value) { + amqp_table_entry_t ret; + ret.key = amqp_cstring_bytes(key); + ret.value.kind = AMQP_FIELD_KIND_TABLE; + ret.value.value.table = *value; + return ret; +} + +amqp_table_entry_t amqp_table_construct_bool_entry(const char *key, + const int value) { + amqp_table_entry_t ret; + ret.key = amqp_cstring_bytes(key); + ret.value.kind = AMQP_FIELD_KIND_BOOLEAN; + ret.value.value.boolean = value; + return ret; +} + +amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table, + const amqp_bytes_t key) { + int i; + assert(table != NULL); + for (i = 0; i < table->num_entries; ++i) { + if (amqp_bytes_equal(table->entries[i].key, key)) { + return &table->entries[i]; + } + } + return NULL; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_table.h b/ext/librabbitmq/librabbitmq/amqp_table.h new file mode 100644 index 000000000..7b009a999 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_table.h @@ -0,0 +1,81 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2014 Alan Antonuk. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ +#ifndef AMQP_TABLE_H +#define AMQP_TABLE_H + +#include "amqp.h" +#include "amqp_private.h" + +/** + * Initializes a table entry with utf-8 string type value. + * + * \param [in] key the table entry key. The string must remain valid for the + * life of the resulting amqp_table_entry_t. + * \param [in] value the string value. The string must remain valid for the life + * of the resulting amqp_table_entry_t. + * \returns An initialized table entry. + */ +amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key, + const char *value); + +/** + * Initializes a table entry with table type value. + * + * \param [in] key the table entry key. The string must remain value for the + * life of the resulting amqp_table_entry_t. + * \param [in] value the amqp_table_t value. The table must remain valid for the + * life of the resulting amqp_table_entry_t. + * \returns An initialized table entry. + */ +amqp_table_entry_t amqp_table_construct_table_entry(const char *key, + const amqp_table_t *value); + +/** + * Initializes a table entry with boolean type value. + * + * \param [in] key the table entry key. The string must remain value for the + * life of the resulting amqp_table_entry_t. + * \param [in] value the boolean value. 0 means false, any other value is true. + * \returns An initialized table entry. + */ +amqp_table_entry_t amqp_table_construct_bool_entry(const char *key, + const int value); + +/** + * Searches a table for an entry with a matching key. + * + * \param [in] table the table to search. + * \param [in] key the string to search with. + * \returns a pointer to the table entry in the table if a matching key can be + * found, NULL otherwise. + */ +amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table, + const amqp_bytes_t key); + +#endif /* AMQP_TABLE_H */ diff --git a/ext/librabbitmq/librabbitmq/amqp_tcp_socket.c b/ext/librabbitmq/librabbitmq/amqp_tcp_socket.c new file mode 100644 index 000000000..12e02cd26 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_tcp_socket.c @@ -0,0 +1,238 @@ +/* + * Copyright 2012-2013 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_private.h" +#include "amqp_tcp_socket.h" + +#include +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#include +#include +#endif +#include +#include + +struct amqp_tcp_socket_t { + const struct amqp_socket_class_t *klass; + int sockfd; + int internal_error; + int state; +}; + +static ssize_t amqp_tcp_socket_send(void *base, const void *buf, size_t len, + int flags) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + ssize_t res; + int flagz = 0; + + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + +#ifdef MSG_NOSIGNAL + flagz |= MSG_NOSIGNAL; +#endif + +#if defined(MSG_MORE) + if (flags & AMQP_SF_MORE) { + flagz |= MSG_MORE; + } +/* Cygwin defines TCP_NOPUSH, but trying to use it will return not + * implemented. Disable it here. */ +#elif defined(TCP_NOPUSH) && !defined(__CYGWIN__) + if (flags & AMQP_SF_MORE && !(self->state & AMQP_SF_MORE)) { + int one = 1; + res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof(one)); + if (0 != res) { + self->internal_error = res; + return AMQP_STATUS_SOCKET_ERROR; + } + self->state |= AMQP_SF_MORE; + } else if (!(flags & AMQP_SF_MORE) && self->state & AMQP_SF_MORE) { + int zero = 0; + res = + setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &zero, sizeof(&zero)); + if (0 != res) { + self->internal_error = res; + res = AMQP_STATUS_SOCKET_ERROR; + } else { + self->state &= ~AMQP_SF_MORE; + } + } +#endif + +start: +#ifdef _WIN32 + res = send(self->sockfd, buf, (int)len, flagz); +#else + res = send(self->sockfd, buf, len, flagz); +#endif + + if (res < 0) { + self->internal_error = amqp_os_socket_error(); + switch (self->internal_error) { + case EINTR: + goto start; +#ifdef _WIN32 + case WSAEWOULDBLOCK: +#else + case EWOULDBLOCK: +#endif +#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif + res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE; + break; + default: + res = AMQP_STATUS_SOCKET_ERROR; + } + } else { + self->internal_error = 0; + } + + return res; +} + +static ssize_t amqp_tcp_socket_recv(void *base, void *buf, size_t len, + int flags) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + ssize_t ret; + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + +start: +#ifdef _WIN32 + ret = recv(self->sockfd, buf, (int)len, flags); +#else + ret = recv(self->sockfd, buf, len, flags); +#endif + + if (0 > ret) { + self->internal_error = amqp_os_socket_error(); + switch (self->internal_error) { + case EINTR: + goto start; +#ifdef _WIN32 + case WSAEWOULDBLOCK: +#else + case EWOULDBLOCK: +#endif +#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif + ret = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD; + break; + default: + ret = AMQP_STATUS_SOCKET_ERROR; + } + } else if (0 == ret) { + ret = AMQP_STATUS_CONNECTION_CLOSED; + } + + return ret; +} + +static int amqp_tcp_socket_open(void *base, const char *host, int port, + struct timeval *timeout) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + if (-1 != self->sockfd) { + return AMQP_STATUS_SOCKET_INUSE; + } + self->sockfd = amqp_open_socket_noblock(host, port, timeout); + if (0 > self->sockfd) { + int err = self->sockfd; + self->sockfd = -1; + return err; + } + return AMQP_STATUS_OK; +} + +static int amqp_tcp_socket_close(void *base, + AMQP_UNUSED amqp_socket_close_enum force) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + if (-1 == self->sockfd) { + return AMQP_STATUS_SOCKET_CLOSED; + } + + if (amqp_os_socket_close(self->sockfd)) { + return AMQP_STATUS_SOCKET_ERROR; + } + self->sockfd = -1; + + return AMQP_STATUS_OK; +} + +static int amqp_tcp_socket_get_sockfd(void *base) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + return self->sockfd; +} + +static void amqp_tcp_socket_delete(void *base) { + struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base; + + if (self) { + amqp_tcp_socket_close(self, AMQP_SC_NONE); + free(self); + } +} + +static const struct amqp_socket_class_t amqp_tcp_socket_class = { + amqp_tcp_socket_send, /* send */ + amqp_tcp_socket_recv, /* recv */ + amqp_tcp_socket_open, /* open */ + amqp_tcp_socket_close, /* close */ + amqp_tcp_socket_get_sockfd, /* get_sockfd */ + amqp_tcp_socket_delete /* delete */ +}; + +amqp_socket_t *amqp_tcp_socket_new(amqp_connection_state_t state) { + struct amqp_tcp_socket_t *self = calloc(1, sizeof(*self)); + if (!self) { + return NULL; + } + self->klass = &amqp_tcp_socket_class; + self->sockfd = -1; + + amqp_set_socket(state, (amqp_socket_t *)self); + + return (amqp_socket_t *)self; +} + +void amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd) { + struct amqp_tcp_socket_t *self; + if (base->klass != &amqp_tcp_socket_class) { + amqp_abort("<%p> is not of type amqp_tcp_socket_t", base); + } + self = (struct amqp_tcp_socket_t *)base; + self->sockfd = sockfd; +} diff --git a/ext/librabbitmq/centos_x64/include/amqp_tcp_socket.h b/ext/librabbitmq/librabbitmq/amqp_tcp_socket.h similarity index 100% rename from ext/librabbitmq/centos_x64/include/amqp_tcp_socket.h rename to ext/librabbitmq/librabbitmq/amqp_tcp_socket.h diff --git a/ext/librabbitmq/librabbitmq/amqp_time.c b/ext/librabbitmq/librabbitmq/amqp_time.c new file mode 100644 index 000000000..7b0a42d06 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_time.c @@ -0,0 +1,265 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "amqp_time.h" +#include "amqp.h" +#include +#include +#include + +#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || \ + defined(__MINGW32__) || defined(__MINGW64__)) +#define AMQP_WIN_TIMER_API +#elif (defined(machintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) +#define AMQP_MAC_TIMER_API +#else +#define AMQP_POSIX_TIMER_API +#endif + +#ifdef AMQP_WIN_TIMER_API +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +uint64_t amqp_get_monotonic_timestamp(void) { + static double NS_PER_COUNT = 0; + LARGE_INTEGER perf_count; + + if (0 == NS_PER_COUNT) { + LARGE_INTEGER perf_frequency; + if (!QueryPerformanceFrequency(&perf_frequency)) { + return 0; + } + NS_PER_COUNT = (double)AMQP_NS_PER_S / perf_frequency.QuadPart; + } + + if (!QueryPerformanceCounter(&perf_count)) { + return 0; + } + + return (uint64_t)(perf_count.QuadPart * NS_PER_COUNT); +} +#endif /* AMQP_WIN_TIMER_API */ + +#ifdef AMQP_MAC_TIMER_API +#include + +uint64_t amqp_get_monotonic_timestamp(void) { + static mach_timebase_info_data_t s_timebase = {0, 0}; + uint64_t timestamp; + + timestamp = mach_absolute_time(); + + if (s_timebase.denom == 0) { + mach_timebase_info(&s_timebase); + if (0 == s_timebase.denom) { + return 0; + } + } + + timestamp *= (uint64_t)s_timebase.numer; + timestamp /= (uint64_t)s_timebase.denom; + + return timestamp; +} +#endif /* AMQP_MAC_TIMER_API */ + +#ifdef AMQP_POSIX_TIMER_API +#include + +uint64_t amqp_get_monotonic_timestamp(void) { +#ifdef __hpux + return (uint64_t)gethrtime(); +#else + struct timespec tp; + if (-1 == clock_gettime(CLOCK_MONOTONIC, &tp)) { + return 0; + } + + return ((uint64_t)tp.tv_sec * AMQP_NS_PER_S + (uint64_t)tp.tv_nsec); +#endif +} +#endif /* AMQP_POSIX_TIMER_API */ + +int amqp_time_from_now(amqp_time_t *time, struct timeval *timeout) { + uint64_t now_ns; + uint64_t delta_ns; + + assert(NULL != time); + + if (NULL == timeout) { + *time = amqp_time_infinite(); + return AMQP_STATUS_OK; + } + if (0 == timeout->tv_sec && 0 == timeout->tv_usec) { + *time = amqp_time_immediate(); + return AMQP_STATUS_OK; + } + + if (timeout->tv_sec < 0 || timeout->tv_usec < 0) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + delta_ns = (uint64_t)timeout->tv_sec * AMQP_NS_PER_S + + (uint64_t)timeout->tv_usec * AMQP_NS_PER_US; + + now_ns = amqp_get_monotonic_timestamp(); + if (0 == now_ns) { + return AMQP_STATUS_TIMER_FAILURE; + } + + time->time_point_ns = now_ns + delta_ns; + if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + return AMQP_STATUS_OK; +} + +int amqp_time_s_from_now(amqp_time_t *time, int seconds) { + uint64_t now_ns; + uint64_t delta_ns; + assert(NULL != time); + + if (0 >= seconds) { + *time = amqp_time_infinite(); + return AMQP_STATUS_OK; + } + + now_ns = amqp_get_monotonic_timestamp(); + if (0 == now_ns) { + return AMQP_STATUS_TIMER_FAILURE; + } + + delta_ns = (uint64_t)seconds * AMQP_NS_PER_S; + time->time_point_ns = now_ns + delta_ns; + if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) { + return AMQP_STATUS_INVALID_PARAMETER; + } + + return AMQP_STATUS_OK; +} + +amqp_time_t amqp_time_immediate(void) { + amqp_time_t time; + time.time_point_ns = 0; + return time; +} + +amqp_time_t amqp_time_infinite(void) { + amqp_time_t time; + time.time_point_ns = UINT64_MAX; + return time; +} + +int amqp_time_ms_until(amqp_time_t time) { + uint64_t now_ns; + uint64_t delta_ns; + int left_ms; + + if (UINT64_MAX == time.time_point_ns) { + return -1; + } + if (0 == time.time_point_ns) { + return 0; + } + + now_ns = amqp_get_monotonic_timestamp(); + if (0 == now_ns) { + return AMQP_STATUS_TIMER_FAILURE; + } + + if (now_ns >= time.time_point_ns) { + return 0; + } + + delta_ns = time.time_point_ns - now_ns; + left_ms = (int)(delta_ns / AMQP_NS_PER_MS); + + return left_ms; +} + +int amqp_time_tv_until(amqp_time_t time, struct timeval *in, + struct timeval **out) { + uint64_t now_ns; + uint64_t delta_ns; + + assert(in != NULL); + if (UINT64_MAX == time.time_point_ns) { + *out = NULL; + return AMQP_STATUS_OK; + } + if (0 == time.time_point_ns) { + in->tv_sec = 0; + in->tv_usec = 0; + *out = in; + return AMQP_STATUS_OK; + } + + now_ns = amqp_get_monotonic_timestamp(); + if (0 == now_ns) { + return AMQP_STATUS_TIMER_FAILURE; + } + + if (now_ns >= time.time_point_ns) { + in->tv_sec = 0; + in->tv_usec = 0; + *out = in; + return AMQP_STATUS_OK; + } + + delta_ns = time.time_point_ns - now_ns; + in->tv_sec = (int)(delta_ns / AMQP_NS_PER_S); + in->tv_usec = (int)((delta_ns % AMQP_NS_PER_S) / AMQP_NS_PER_US); + *out = in; + + return AMQP_STATUS_OK; +} + +int amqp_time_has_past(amqp_time_t time) { + uint64_t now_ns; + if (UINT64_MAX == time.time_point_ns) { + return AMQP_STATUS_OK; + } + + now_ns = amqp_get_monotonic_timestamp(); + if (0 == now_ns) { + return AMQP_STATUS_TIMER_FAILURE; + } + + if (now_ns > time.time_point_ns) { + return AMQP_STATUS_TIMEOUT; + } + return AMQP_STATUS_OK; +} + +amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r) { + if (l.time_point_ns < r.time_point_ns) { + return l; + } + return r; +} + +int amqp_time_equal(amqp_time_t l, amqp_time_t r) { + return l.time_point_ns == r.time_point_ns; +} diff --git a/ext/librabbitmq/librabbitmq/amqp_time.h b/ext/librabbitmq/librabbitmq/amqp_time.h new file mode 100644 index 000000000..194bf6715 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_time.h @@ -0,0 +1,130 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef AMQP_TIMER_H +#define AMQP_TIMER_H + +#include + +#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__))) +#ifndef WINVER +#define WINVER 0x0502 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif + +#define AMQP_MS_PER_S 1000 +#define AMQP_US_PER_MS 1000 +#define AMQP_NS_PER_S 1000000000 +#define AMQP_NS_PER_MS 1000000 +#define AMQP_NS_PER_US 1000 + +/* This represents a point in time in reference to a monotonic clock. + * + * The internal representation is ns, relative to the monotonic clock. + * + * There are two 'special' values: + * - 0: means 'this instant', its meant for polls with a 0-timeout, or + * non-blocking option + * - UINT64_MAX: means 'at infinity', its mean for polls with an infinite + * timeout + */ +typedef struct amqp_time_t_ { uint64_t time_point_ns; } amqp_time_t; + +/* Gets a monotonic timestamp. This will return 0 if the underlying call to the + * system fails. + */ +uint64_t amqp_get_monotonic_timestamp(void); + +/* Get a amqp_time_t that is timeout from now. + * If timeout is NULL, an amqp_time_infinite() is created. + * If timeout = {0, 0}, an amqp_time_immediate() is created. + * + * Returns AMQP_STATUS_OK on success. + * AMQP_STATUS_INVALID_PARAMETER if timeout is invalid + * AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp + * fails. + */ +int amqp_time_from_now(amqp_time_t *time, struct timeval *timeout); + +/* Get a amqp_time_t that is seconds from now. + * If seconds <= 0, then amqp_time_infinite() is created. + * + * Returns AMQP_STATUS_OK on success. + * AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp + * fails. + */ +int amqp_time_s_from_now(amqp_time_t *time, int seconds); + +/* Create an immediate amqp_time_t */ +amqp_time_t amqp_time_immediate(void); + +/* Create an infinite amqp_time_t */ +amqp_time_t amqp_time_infinite(void); + +/* Gets the number of ms until the amqp_time_t, suitable for the timeout + * parameter in poll(). + * + * -1 will be returned for amqp_time_infinite values. + * 0 will be returned for amqp_time_immediate values. + * AMQP_STATUS_TIMEOUT will be returned if time was in the past. + * AMQP_STATUS_TIMER_FAILURE will be returned if the underlying call to get the + * current timestamp fails. + */ +int amqp_time_ms_until(amqp_time_t time); + +/* Gets a timeval filled in with the time until amqp_time_t. Suitable for the + * parameter in select(). + * + * The in parameter specifies a storage location for *out. + * If time is an inf timeout, then *out = NULL. + * If time is a 0-timeout or the timer has expired, then *out = {0, 0} + * Otherwise *out is set to the time left on the time. + * + * AMQP_STATUS_OK will be returned if successfully filled. + * AMQP_STATUS_TIMER_FAILURE is returned when the underlying call to get the + * current timestamp fails. + */ +int amqp_time_tv_until(amqp_time_t time, struct timeval *in, + struct timeval **out); + +/* Test whether current time is past the provided time. + * + * TODO: this isn't a great interface to use. Fix this. + * + * Return AMQP_STATUS_OK if time has not past + * Return AMQP_STATUS_TIMEOUT if time has past + * Return AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current + * timestamp fails. + */ +int amqp_time_has_past(amqp_time_t time); + +/* Return the time value that happens first */ +amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r); + +int amqp_time_equal(amqp_time_t l, amqp_time_t r); +#endif /* AMQP_TIMER_H */ diff --git a/ext/librabbitmq/librabbitmq/amqp_url.c b/ext/librabbitmq/librabbitmq/amqp_url.c new file mode 100644 index 000000000..b5304e545 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/amqp_url.c @@ -0,0 +1,220 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "amqp_private.h" +#include +#include +#include +#include +#include + +void amqp_default_connection_info(struct amqp_connection_info *ci) { + /* Apply defaults */ + ci->user = "guest"; + ci->password = "guest"; + ci->host = "localhost"; + ci->port = 5672; + ci->vhost = "/"; + ci->ssl = 0; +} + +/* Scan for the next delimiter, handling percent-encodings on the way. */ +static char find_delim(char **pp, int colon_and_at_sign_are_delims) { + char *from = *pp; + char *to = from; + + for (;;) { + char ch = *from++; + + switch (ch) { + case ':': + case '@': + if (!colon_and_at_sign_are_delims) { + *to++ = ch; + break; + } + + /* fall through */ + case 0: + case '/': + case '?': + case '#': + case '[': + case ']': + *to = 0; + *pp = from; + return ch; + + case '%': { + unsigned int val; + int chars; + int res = sscanf(from, "%2x%n", &val, &chars); + + if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX) + /* Return a surprising delimiter to + force an error. */ + { + return '%'; + } + + *to++ = (char)val; + from += 2; + break; + } + + default: + *to++ = ch; + break; + } + } +} + +/* Parse an AMQP URL into its component parts. */ +int amqp_parse_url(char *url, struct amqp_connection_info *parsed) { + int res = AMQP_STATUS_BAD_URL; + char delim; + char *start; + char *host; + char *port = NULL; + + amqp_default_connection_info(parsed); + + /* check the prefix */ + if (!strncmp(url, "amqp://", 7)) { + /* do nothing */ + } else if (!strncmp(url, "amqps://", 8)) { + parsed->port = 5671; + parsed->ssl = 1; + } else { + goto out; + } + + host = start = url += (parsed->ssl ? 8 : 7); + delim = find_delim(&url, 1); + + if (delim == ':') { + /* The colon could be introducing the port or the + password part of the userinfo. We don't know yet, + so stash the preceding component. */ + port = start = url; + delim = find_delim(&url, 1); + } + + if (delim == '@') { + /* What might have been the host and port were in fact + the username and password */ + parsed->user = host; + if (port) { + parsed->password = port; + } + + port = NULL; + host = start = url; + delim = find_delim(&url, 1); + } + + if (delim == '[') { + /* IPv6 address. The bracket should be the first + character in the host. */ + if (host != start || *host != 0) { + goto out; + } + + start = url; + delim = find_delim(&url, 0); + + if (delim != ']') { + goto out; + } + + parsed->host = start; + start = url; + delim = find_delim(&url, 1); + + /* Closing bracket should be the last character in the + host. */ + if (*start != 0) { + goto out; + } + } else { + /* If we haven't seen the host yet, this is it. */ + if (*host != 0) { + parsed->host = host; + } + } + + if (delim == ':') { + port = start = url; + delim = find_delim(&url, 1); + } + + if (port) { + char *end; + long portnum = strtol(port, &end, 10); + + if (port == end || *end != 0 || portnum < 0 || portnum > 65535) { + goto out; + } + + parsed->port = portnum; + } + + if (delim == '/') { + start = url; + delim = find_delim(&url, 1); + + if (delim != 0) { + goto out; + } + + parsed->vhost = start; + res = AMQP_STATUS_OK; + } else if (delim == 0) { + res = AMQP_STATUS_OK; + } + +/* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */ + +out: + return res; +} diff --git a/ext/librabbitmq/librabbitmq/codegen.py b/ext/librabbitmq/librabbitmq/codegen.py new file mode 100644 index 000000000..3ae24b634 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/codegen.py @@ -0,0 +1,785 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MIT +# +# Portions created by Alan Antonuk are Copyright (c) 2012-2013 +# Alan Antonuk. All Rights Reserved. +# +# Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. +# All Rights Reserved. +# +# Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 +# VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ***** END LICENSE BLOCK ***** + +from __future__ import nested_scopes +from __future__ import division + +from amqp_codegen import * +import string +import re + + +class Emitter(object): + """An object the trivially emits generated code lines. + + This largely exists to be wrapped by more sophisticated emitter + classes. + """ + + def __init__(self, prefix): + self.prefix = prefix + + def emit(self, line): + """Emit a line of generated code.""" + print self.prefix + line + + +class BitDecoder(object): + """An emitter object that keeps track of the state involved in + decoding the AMQP bit type.""" + + def __init__(self, emitter): + self.emitter = emitter + self.bit = 0 + + def emit(self, line): + self.bit = 0 + self.emitter.emit(line) + + def decode_bit(self, lvalue): + """Generate code to decode a value of the AMQP bit type into + the given lvalue.""" + if self.bit == 0: + self.emitter.emit("if (!amqp_decode_8(encoded, &offset, &bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;") + + self.emitter.emit("%s = (bit_buffer & (1 << %d)) ? 1 : 0;" + % (lvalue, self.bit)) + self.bit += 1 + if self.bit == 8: + self.bit = 0 + + +class BitEncoder(object): + """An emitter object that keeps track of the state involved in + encoding the AMQP bit type.""" + + def __init__(self, emitter): + self.emitter = emitter + self.bit = 0 + + def flush(self): + """Flush the state associated with AMQP bit types.""" + if self.bit: + self.emitter.emit("if (!amqp_encode_8(encoded, &offset, bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;") + self.bit = 0 + + def emit(self, line): + self.flush() + self.emitter.emit(line) + + def encode_bit(self, value): + """Generate code to encode a value of the AMQP bit type from + the given value.""" + if self.bit == 0: + self.emitter.emit("bit_buffer = 0;") + + self.emitter.emit("if (%s) bit_buffer |= (1 << %d);" + % (value, self.bit)) + self.bit += 1 + if self.bit == 8: + self.flush() + + +class SimpleType(object): + """A AMQP type that corresponds to a simple scalar C value of a + certain width.""" + + def __init__(self, bits): + self.bits = bits + self.ctype = "uint%d_t" % (bits,) + + def decode(self, emitter, lvalue): + emitter.emit("if (!amqp_decode_%d(encoded, &offset, &%s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, lvalue)) + + def encode(self, emitter, value): + emitter.emit("if (!amqp_encode_%d(encoded, &offset, %s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, value)) + + def literal(self, value): + return value + +class StrType(object): + """The AMQP shortstr or longstr types.""" + + def __init__(self, lenbits): + self.lenbits = lenbits + self.ctype = "amqp_bytes_t" + + def decode(self, emitter, lvalue): + emitter.emit("{") + emitter.emit(" uint%d_t len;" % (self.lenbits,)) + emitter.emit(" if (!amqp_decode_%d(encoded, &offset, &len)" % (self.lenbits,)) + emitter.emit(" || !amqp_decode_bytes(encoded, &offset, &%s, len))" % (lvalue,)) + emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;") + emitter.emit("}") + + def encode(self, emitter, value): + emitter.emit("if (UINT%d_MAX < %s.len" % (self.lenbits, value)) + emitter.emit(" || !amqp_encode_%d(encoded, &offset, (uint%d_t)%s.len)" % + (self.lenbits, self.lenbits, value)) + emitter.emit(" || !amqp_encode_bytes(encoded, &offset, %s))" % (value,)) + emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;") + + def literal(self, value): + if value != '': + raise NotImplementedError() + + return "amqp_empty_bytes" + +class BitType(object): + """The AMQP bit type.""" + + def __init__(self): + self.ctype = "amqp_boolean_t" + + def decode(self, emitter, lvalue): + emitter.decode_bit(lvalue) + + def encode(self, emitter, value): + emitter.encode_bit(value) + + def literal(self, value): + return {True: 1, False: 0}[value] + +class TableType(object): + """The AMQP table type.""" + + def __init__(self): + self.ctype = "amqp_table_t" + + def decode(self, emitter, lvalue): + emitter.emit("{") + emitter.emit(" int res = amqp_decode_table(encoded, pool, &(%s), &offset);" % (lvalue,)) + emitter.emit(" if (res < 0) return res;") + emitter.emit("}") + + def encode(self, emitter, value): + emitter.emit("{") + emitter.emit(" int res = amqp_encode_table(encoded, &(%s), &offset);" % (value,)) + emitter.emit(" if (res < 0) return res;") + emitter.emit("}") + + def literal(self, value): + raise NotImplementedError() + +types = { + 'octet': SimpleType(8), + 'short': SimpleType(16), + 'long': SimpleType(32), + 'longlong': SimpleType(64), + 'shortstr': StrType(8), + 'longstr': StrType(32), + 'bit': BitType(), + 'table': TableType(), + 'timestamp': SimpleType(64), +} + +def typeFor(spec, f): + """Get a representation of the AMQP type of a field.""" + return types[spec.resolveDomain(f.domain)] + +def c_ize(s): + s = s.replace('-', '_') + s = s.replace(' ', '_') + return s + +# When generating API functions corresponding to synchronous methods, +# we need some information that isn't in the protocol def: Some +# methods should not be exposed, indicated here by a False value. +# Some methods should be exposed but certain fields should not be +# exposed as parameters. +apiMethodInfo = { + "amqp_connection_start": False, # application code should not use this + "amqp_connection_secure": False, # application code should not use this + "amqp_connection_tune": False, # application code should not use this + "amqp_connection_open": False, # application code should not use this + "amqp_connection_close": False, # needs special handling + "amqp_channel_open": ["out_of_band"], + "amqp_channel_close": False, # needs special handling + "amqp_access_request": False, # huh? + "amqp_basic_get": False, # get-ok has content +} + +# When generating API functions corresponding to synchronous methods, +# some fields should be suppressed everywhere. This dict names those +# fields, and the fixed values to use for them. +apiMethodsSuppressArgs = {"ticket": 0, "nowait": False} + +AmqpMethod.defName = lambda m: cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + "_method") +AmqpMethod.fullName = lambda m: "amqp_%s_%s" % (c_ize(m.klass.name), c_ize(m.name)) +AmqpMethod.structName = lambda m: m.fullName() + "_t" + +AmqpClass.structName = lambda c: "amqp_" + c_ize(c.name) + "_properties_t" + +def methodApiPrototype(m): + fn = m.fullName() + info = apiMethodInfo.get(fn, []) + + docs = "/**\n * %s\n *\n" % (fn) + docs += " * @param [in] state connection state\n" + docs += " * @param [in] channel the channel to do the RPC on\n" + + args = [] + for f in m.arguments: + n = c_ize(f.name) + if n in apiMethodsSuppressArgs or n in info: + continue + + args.append(", ") + args.append(typeFor(m.klass.spec, f).ctype) + args.append(" ") + args.append(n) + docs += " * @param [in] %s %s\n" % (n, n) + + docs += " * @returns %s_ok_t\n" % (fn) + docs += " */\n" + + return "%sAMQP_PUBLIC_FUNCTION\n%s_ok_t *\nAMQP_CALL %s(amqp_connection_state_t state, amqp_channel_t channel%s)" % (docs, fn, fn, ''.join(args)) + +AmqpMethod.apiPrototype = methodApiPrototype + +def cConstantName(s): + return 'AMQP_' + '_'.join(re.split('[- ]', s.upper())) + +def cFlagName(c, f): + return cConstantName(c.name + '_' + f.name) + '_FLAG' + +def genErl(spec): + def fieldTempList(fields): + return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']' + + def fieldMapList(fields): + return ', '.join([c_ize(f.name) + " = F" + str(f.index) for f in fields]) + + def genLookupMethodName(m): + print ' case %s: return "%s";' % (m.defName(), m.defName()) + + def genDecodeMethodFields(m): + print " case %s: {" % (m.defName(),) + print " %s *m = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \ + (m.structName(), m.structName(), m.structName()) + print " if (m == NULL) { return AMQP_STATUS_NO_MEMORY; }" + + emitter = BitDecoder(Emitter(" ")) + for f in m.arguments: + typeFor(spec, f).decode(emitter, "m->"+c_ize(f.name)) + + print " *decoded = m;" + print " return 0;" + print " }" + + def genDecodeProperties(c): + print " case %d: {" % (c.index,) + print " %s *p = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \ + (c.structName(), c.structName(), c.structName()) + print " if (p == NULL) { return AMQP_STATUS_NO_MEMORY; }" + print " p->_flags = flags;" + + emitter = Emitter(" ") + for f in c.fields: + emitter.emit("if (flags & %s) {" % (cFlagName(c, f),)) + typeFor(spec, f).decode(emitter, "p->"+c_ize(f.name)) + emitter.emit("}") + + print " *decoded = p;" + print " return 0;" + print " }" + + def genEncodeMethodFields(m): + print " case %s: {" % (m.defName(),) + if m.arguments: + print " %s *m = (%s *) decoded;" % (m.structName(), m.structName()) + + emitter = BitEncoder(Emitter(" ")) + for f in m.arguments: + typeFor(spec, f).encode(emitter, "m->"+c_ize(f.name)) + emitter.flush() + + print " return (int)offset;" + print " }" + + def genEncodeProperties(c): + print " case %d: {" % (c.index,) + if c.fields: + print " %s *p = (%s *) decoded;" % (c.structName(), c.structName()) + + emitter = Emitter(" ") + for f in c.fields: + emitter.emit(" if (flags & %s) {" % (cFlagName(c, f),)) + typeFor(spec, f).encode(emitter, "p->"+c_ize(f.name)) + emitter.emit("}") + + print " return (int)offset;" + print " }" + + methods = spec.allMethods() + + print """/* Generated code. Do not edit. Edit and re-run codegen.py instead. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_private.h" +#include +#include +#include +#include +""" + + print """ +char const *amqp_constant_name(int constantNumber) { + switch (constantNumber) {""" + for (c,v,cls) in spec.constants: + print " case %s: return \"%s\";" % (cConstantName(c), cConstantName(c)) + print """ default: return "(unknown)"; + } +}""" + + print """ +amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) { + switch (constantNumber) {""" + for (c,v,cls) in spec.constants: + if cls == 'hard-error': + print " case %s: return 1;" % (cConstantName(c),) + print """ default: return 0; + } +}""" + + print """ +char const *amqp_method_name(amqp_method_number_t methodNumber) { + switch (methodNumber) {""" + for m in methods: genLookupMethodName(m) + print """ default: return NULL; + } +}""" + + print """ +amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) { + switch (methodNumber) {""" + for m in methods: + if m.hasContent: + print ' case %s: return 1;' % (m.defName()) + print """ default: return 0; + } +}""" + + print """ +int amqp_decode_method(amqp_method_number_t methodNumber, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded) +{ + size_t offset = 0; + uint8_t bit_buffer; + + switch (methodNumber) {""" + for m in methods: genDecodeMethodFields(m) + print """ default: return AMQP_STATUS_UNKNOWN_METHOD; + } +}""" + + print """ +int amqp_decode_properties(uint16_t class_id, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded) +{ + size_t offset = 0; + + amqp_flags_t flags = 0; + int flagword_index = 0; + uint16_t partial_flags; + + do { + if (!amqp_decode_16(encoded, &offset, &partial_flags)) + return AMQP_STATUS_BAD_AMQP_DATA; + flags |= (partial_flags << (flagword_index * 16)); + flagword_index++; + } while (partial_flags & 1); + + switch (class_id) {""" + for c in spec.allClasses(): genDecodeProperties(c) + print """ default: return AMQP_STATUS_UNKNOWN_CLASS; + } +}""" + + print """ +int amqp_encode_method(amqp_method_number_t methodNumber, + void *decoded, + amqp_bytes_t encoded) +{ + size_t offset = 0; + uint8_t bit_buffer; + + switch (methodNumber) {""" + for m in methods: genEncodeMethodFields(m) + print """ default: return AMQP_STATUS_UNKNOWN_METHOD; + } +}""" + + print """ +int amqp_encode_properties(uint16_t class_id, + void *decoded, + amqp_bytes_t encoded) +{ + size_t offset = 0; + + /* Cheat, and get the flags out generically, relying on the + similarity of structure between classes */ + amqp_flags_t flags = * (amqp_flags_t *) decoded; /* cheating! */ + + { + /* We take a copy of flags to avoid destroying it, as it is used + in the autogenerated code below. */ + amqp_flags_t remaining_flags = flags; + do { + amqp_flags_t remainder = remaining_flags >> 16; + uint16_t partial_flags = remaining_flags & 0xFFFE; + if (remainder != 0) { partial_flags |= 1; } + if (!amqp_encode_16(encoded, &offset, partial_flags)) + return AMQP_STATUS_BAD_AMQP_DATA; + remaining_flags = remainder; + } while (remaining_flags != 0); + } + + switch (class_id) {""" + for c in spec.allClasses(): genEncodeProperties(c) + print """ default: return AMQP_STATUS_UNKNOWN_CLASS; + } +}""" + + for m in methods: + if not m.isSynchronous: + continue + + info = apiMethodInfo.get(m.fullName(), []) + if info is False: + continue + + print + print m.apiPrototype() + print "{" + print " %s req;" % (m.structName(),) + + for f in m.arguments: + n = c_ize(f.name) + + val = apiMethodsSuppressArgs.get(n) + if val is None and n in info: + val = f.defaultvalue + + if val is None: + val = n + else: + val = typeFor(spec, f).literal(val) + + + print " req.%s = %s;" % (n, val) + + reply = cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + + "_ok_method") + print """ + return amqp_simple_rpc_decoded(state, channel, %s, %s, &req); +} +""" % (m.defName(), reply) + +def genHrl(spec): + def fieldDeclList(fields): + if fields: + return ''.join([" %s %s; /**< %s */\n" % (typeFor(spec, f).ctype, + c_ize(f.name), f.name) + for f in fields]) + else: + return " char dummy; /**< Dummy field to avoid empty struct */\n" + + def propDeclList(fields): + return ''.join([" %s %s;\n" % (typeFor(spec, f).ctype, c_ize(f.name)) + for f in fields + if spec.resolveDomain(f.domain) != 'bit']) + + methods = spec.allMethods() + + print """/* Generated code. Do not edit. Edit and re-run codegen.py instead. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +/** @file amqp_framing.h */ +#ifndef AMQP_FRAMING_H +#define AMQP_FRAMING_H + +#include + +AMQP_BEGIN_DECLS +""" + print "#define AMQP_PROTOCOL_VERSION_MAJOR %d /**< AMQP protocol version major */" % (spec.major) + print "#define AMQP_PROTOCOL_VERSION_MINOR %d /**< AMQP protocol version minor */" % (spec.minor) + print "#define AMQP_PROTOCOL_VERSION_REVISION %d /**< AMQP protocol version revision */" % (spec.revision) + print "#define AMQP_PROTOCOL_PORT %d /**< Default AMQP Port */" % (spec.port) + + for (c,v,cls) in spec.constants: + print "#define %s %s /**< Constant: %s */" % (cConstantName(c), v, c) + print + + print """/* Function prototypes. */ + +/** + * Get constant name string from constant + * + * @param [in] constantNumber constant to get the name of + * @returns string describing the constant. String is managed by + * the library and should not be free()'d by the program + */ +AMQP_PUBLIC_FUNCTION +char const * +AMQP_CALL amqp_constant_name(int constantNumber); + +/** + * Checks to see if a constant is a hard error + * + * A hard error occurs when something severe enough + * happens that the connection must be closed. + * + * @param [in] constantNumber the error constant + * @returns true if its a hard error, false otherwise + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_constant_is_hard_error(int constantNumber); + +/** + * Get method name string from method number + * + * @param [in] methodNumber the method number + * @returns method name string. String is managed by the library + * and should not be freed()'d by the program + */ +AMQP_PUBLIC_FUNCTION +char const * +AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber); + +/** + * Check whether a method has content + * + * A method that has content will receive the method frame + * a properties frame, then 1 to N body frames + * + * @param [in] methodNumber the method number + * @returns true if method has content, false otherwise + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber); + +/** + * Decodes a method from AMQP wireformat + * + * @param [in] methodNumber the method number for the decoded parameter + * @param [in] pool the memory pool to allocate the decoded method from + * @param [in] encoded the encoded byte string buffer + * @param [out] decoded pointer to the decoded method struct + * @returns 0 on success, an error code otherwise + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded); + +/** + * Decodes a header frame properties structure from AMQP wireformat + * + * @param [in] class_id the class id for the decoded parameter + * @param [in] pool the memory pool to allocate the decoded properties from + * @param [in] encoded the encoded byte string buffer + * @param [out] decoded pointer to the decoded properties struct + * @returns 0 on success, an error code otherwise + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_decode_properties(uint16_t class_id, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded); + +/** + * Encodes a method structure in AMQP wireformat + * + * @param [in] methodNumber the method number for the decoded parameter + * @param [in] decoded the method structure (e.g., amqp_connection_start_t) + * @param [in] encoded an allocated byte buffer for the encoded method + * structure to be written to. If the buffer isn't large enough + * to hold the encoded method, an error code will be returned. + * @returns 0 on success, an error code otherwise. + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber, + void *decoded, + amqp_bytes_t encoded); + +/** + * Encodes a properties structure in AMQP wireformat + * + * @param [in] class_id the class id for the decoded parameter + * @param [in] decoded the properties structure (e.g., amqp_basic_properties_t) + * @param [in] encoded an allocated byte buffer for the encoded properties to written to. + * If the buffer isn't large enough to hold the encoded method, an + * an error code will be returned + * @returns 0 on success, an error code otherwise. + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_encode_properties(uint16_t class_id, + void *decoded, + amqp_bytes_t encoded); +""" + + print "/* Method field records. */\n" + for m in methods: + methodid = m.klass.index << 16 | m.index + print "#define %s ((amqp_method_number_t) 0x%.08X) /**< %s.%s method id @internal %d, %d; %d */" % \ + (m.defName(), + methodid, + m.klass.name, + m.name, + m.klass.index, + m.index, + methodid) + print "/** %s.%s method fields */\ntypedef struct %s_ {\n%s} %s;\n" % \ + (m.klass.name, m.name, m.structName(), fieldDeclList(m.arguments), m.structName()) + + print "/* Class property records. */" + for c in spec.allClasses(): + print "#define %s (0x%.04X) /**< %s class id @internal %d */" % \ + (cConstantName(c.name + "_class"), c.index, c.name, c.index) + index = 0 + for f in c.fields: + if index % 16 == 15: + index = index + 1 + shortnum = index // 16 + partialindex = 15 - (index % 16) + bitindex = shortnum * 16 + partialindex + print '#define %s (1 << %d) /**< %s.%s property flag */' % (cFlagName(c, f), bitindex, c.name, f.name) + index = index + 1 + print "/** %s class properties */\ntypedef struct %s_ {\n amqp_flags_t _flags; /**< bit-mask of set fields */\n%s} %s;\n" % \ + (c.name, + c.structName(), + fieldDeclList(c.fields), + c.structName()) + + print "/* API functions for methods */\n" + + for m in methods: + if m.isSynchronous and apiMethodInfo.get(m.fullName()) is not False: + print "%s;" % (m.apiPrototype(),) + + print """ +AMQP_END_DECLS + +#endif /* AMQP_FRAMING_H */""" + +def generateErl(specPath): + genErl(AmqpSpec(specPath)) + +def generateHrl(specPath): + genHrl(AmqpSpec(specPath)) + +if __name__ == "__main__": + do_main(generateHrl, generateErl) diff --git a/ext/librabbitmq/librabbitmq/unix/threads.h b/ext/librabbitmq/librabbitmq/unix/threads.h new file mode 100644 index 000000000..8a2623bbe --- /dev/null +++ b/ext/librabbitmq/librabbitmq/unix/threads.h @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2013 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AMQP_THREADS_H +#define AMQP_THREADS_H + +#include + +#endif /* AMQP_THREADS_H */ diff --git a/ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h b/ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h new file mode 100644 index 000000000..a7437be66 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h @@ -0,0 +1,245 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +#define _W64 __w64 +#else +#define _W64 +#endif +#endif + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#else +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ +typedef signed __int64 intptr_t; +typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ +typedef _W64 signed int intptr_t; +typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || \ + defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and + // footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +#define PTRDIFF_MIN _I64_MIN +#define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +#define PTRDIFF_MIN _I32_MIN +#define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +#ifdef _WIN64 // [ +#define SIZE_MAX _UI64_MAX +#else // _WIN64 ][ +#define SIZE_MAX _UI32_MAX +#endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +#define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +#define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || \ + defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_STDINT_H_ ] diff --git a/ext/librabbitmq/librabbitmq/win32/threads.c b/ext/librabbitmq/librabbitmq/win32/threads.c new file mode 100644 index 000000000..cce315865 --- /dev/null +++ b/ext/librabbitmq/librabbitmq/win32/threads.c @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2013 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "threads.h" + +#include + +DWORD pthread_self(void) { return GetCurrentThreadId(); } + +int pthread_mutex_init(pthread_mutex_t *mutex, void *attr) { + if (!mutex) { + return 1; + } + InitializeSRWLock(mutex); + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + if (!mutex) { + return 1; + } + AcquireSRWLockExclusive(mutex); + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + if (!mutex) { + return 1; + } + ReleaseSRWLockExclusive(mutex); + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) { + /* SRW's do not require destruction. */ + return 0; +} diff --git a/ext/librabbitmq/librabbitmq/win32/threads.h b/ext/librabbitmq/librabbitmq/win32/threads.h new file mode 100644 index 000000000..69371f39b --- /dev/null +++ b/ext/librabbitmq/librabbitmq/win32/threads.h @@ -0,0 +1,52 @@ +/* + * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk. + * All Rights Reserved. + * + * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael + * Steinert. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AMQP_THREAD_H +#define AMQP_THREAD_H + +#if !defined(WINVER) || defined(__MINGW32__) || defined(__MINGW64__) +#ifdef WINVER +#undef WINVER +#endif +/* Windows Vista or newer */ +#define WINVER 0x0600 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +typedef SRWLOCK pthread_mutex_t; +#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT; + +DWORD pthread_self(void); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); +int pthread_mutex_unlock(pthread_mutex_t *); +int pthread_mutex_destroy(pthread_mutex_t *); + +#endif /* AMQP_THREAD_H */ diff --git a/ext/librabbitmq/tests/CMakeLists.txt b/ext/librabbitmq/tests/CMakeLists.txt new file mode 100644 index 000000000..cf042b045 --- /dev/null +++ b/ext/librabbitmq/tests/CMakeLists.txt @@ -0,0 +1,47 @@ +include_directories(${LIBRABBITMQ_INCLUDE_DIRS}) + +if (MSVC) + # No version of MSVC has inttypes.h, this uses the msinttypes + # Note this includes stdint.h which is either in + # ../librabbitmq/win32/msinttypes or in a standard location + include_directories(win32/msinttypes) +endif (MSVC) + +add_definitions(-DHAVE_CONFIG_H) +add_definitions(-DAMQP_STATIC) + +add_executable(test_parse_url test_parse_url.c) +target_link_libraries(test_parse_url rabbitmq-static) +add_test(parse_url test_parse_url) + +add_executable(test_tables test_tables.c) +target_link_libraries(test_tables rabbitmq-static) +add_test(tables test_tables) +configure_file(test_tables.expected ${CMAKE_CURRENT_BINARY_DIR}/tests/test_tables.expected COPYONLY) + +add_executable(test_hostcheck + test_hostcheck.c + ../librabbitmq/amqp_hostcheck.c) +add_test(hostcheck test_hostcheck) + +add_executable(test_status_enum + test_status_enum.c) +target_link_libraries(test_status_enum rabbitmq-static) +add_test(status_enum test_status_enum) + +add_executable(test_basic + test_basic.c) +target_link_libraries(test_basic rabbitmq-static) + +if (NOT APPLE) + add_test(basic test_basic) +endif() + +add_executable(test_sasl_mechanism test_sasl_mechanism.c) +target_link_libraries(test_sasl_mechanism rabbitmq-static) +add_test(sasl_mechanism test_sasl_mechanism) + +add_executable(test_merge_capabilities test_merge_capabilities.c) +target_link_libraries(test_merge_capabilities rabbitmq-static) +add_test(merge_capabilities test_merge_capabilities) + diff --git a/ext/librabbitmq/tests/test_basic.c b/ext/librabbitmq/tests/test_basic.c new file mode 100644 index 000000000..a7de0446f --- /dev/null +++ b/ext/librabbitmq/tests/test_basic.c @@ -0,0 +1,207 @@ +/* + * Copyright 2017 Simon Giesecke + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "amqp.h" +#include "amqp_tcp_socket.h" +#include "amqp_time.h" + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include + +static const int fixed_channel_id = 1; +static const char test_queue_name[] = "test_queue"; + +amqp_connection_state_t setup_connection_and_channel(void) { + amqp_connection_state_t connection_state_ = amqp_new_connection(); + + amqp_socket_t *socket = amqp_tcp_socket_new(connection_state_); + assert(socket); + + int rc = amqp_socket_open(socket, "localhost", AMQP_PROTOCOL_PORT); + assert(rc == AMQP_STATUS_OK); + + amqp_rpc_reply_t rpc_reply = amqp_login( + connection_state_, "/", 1, AMQP_DEFAULT_FRAME_SIZE, + AMQP_DEFAULT_HEARTBEAT, AMQP_SASL_METHOD_PLAIN, "guest", "guest"); + assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL); + + amqp_channel_open_ok_t *res = + amqp_channel_open(connection_state_, fixed_channel_id); + assert(res != NULL); + + return connection_state_; +} + +void close_and_destroy_connection(amqp_connection_state_t connection_state_) { + amqp_rpc_reply_t rpc_reply = + amqp_connection_close(connection_state_, AMQP_REPLY_SUCCESS); + assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL); + + int rc = amqp_destroy_connection(connection_state_); + assert(rc == AMQP_STATUS_OK); +} + +void basic_publish(amqp_connection_state_t connectionState_, + const char *message_) { + amqp_bytes_t message_bytes = amqp_cstring_bytes(message_); + + amqp_basic_properties_t properties; + properties._flags = 0; + + properties._flags |= AMQP_BASIC_DELIVERY_MODE_FLAG; + properties.delivery_mode = AMQP_DELIVERY_NONPERSISTENT; + + int retval = amqp_basic_publish( + connectionState_, fixed_channel_id, amqp_cstring_bytes(""), + amqp_cstring_bytes(test_queue_name), + /* mandatory=*/1, + /* immediate=*/0, /* RabbitMQ 3.x does not support the "immediate" flag + according to + https://www.rabbitmq.com/specification.html */ + &properties, message_bytes); + + assert(retval == 0); +} + +void queue_declare(amqp_connection_state_t connection_state_, + const char *queue_name_) { + amqp_queue_declare_ok_t *res = amqp_queue_declare( + connection_state_, fixed_channel_id, amqp_cstring_bytes(queue_name_), + /*passive*/ 0, + /*durable*/ 0, + /*exclusive*/ 0, + /*auto_delete*/ 1, amqp_empty_table); + assert(res != NULL); +} + +char *basic_get(amqp_connection_state_t connection_state_, + const char *queue_name_, uint64_t *out_body_size_) { + amqp_rpc_reply_t rpc_reply; + amqp_time_t deadline; + struct timeval timeout = {5, 0}; + int time_rc = amqp_time_from_now(&deadline, &timeout); + assert(time_rc == AMQP_STATUS_OK); + + do { + rpc_reply = amqp_basic_get(connection_state_, fixed_channel_id, + amqp_cstring_bytes(queue_name_), /*no_ack*/ 1); + } while (rpc_reply.reply_type == AMQP_RESPONSE_NORMAL && + rpc_reply.reply.id == AMQP_BASIC_GET_EMPTY_METHOD && + amqp_time_has_past(deadline) == AMQP_STATUS_OK); + + assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL); + assert(rpc_reply.reply.id == AMQP_BASIC_GET_OK_METHOD); + + amqp_message_t message; + rpc_reply = + amqp_read_message(connection_state_, fixed_channel_id, &message, 0); + assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL); + + char *body = malloc(message.body.len); + memcpy(body, message.body.bytes, message.body.len); + *out_body_size_ = message.body.len; + amqp_destroy_message(&message); + + return body; +} + +void publish_and_basic_get_message(const char *msg_to_publish) { + amqp_connection_state_t connection_state = setup_connection_and_channel(); + + queue_declare(connection_state, test_queue_name); + basic_publish(connection_state, msg_to_publish); + + uint64_t body_size; + char *msg = basic_get(connection_state, test_queue_name, &body_size); + + assert(body_size == strlen(msg_to_publish)); + assert(strncmp(msg_to_publish, msg, body_size) == 0); + free(msg); + + close_and_destroy_connection(connection_state); +} + +char *consume_message(amqp_connection_state_t connection_state_, + const char *queue_name_, uint64_t *out_body_size_) { + amqp_basic_consume_ok_t *result = + amqp_basic_consume(connection_state_, fixed_channel_id, + amqp_cstring_bytes(queue_name_), amqp_empty_bytes, + /*no_local*/ 0, + /*no_ack*/ 1, + /*exclusive*/ 0, amqp_empty_table); + assert(result != NULL); + + amqp_envelope_t envelope; + struct timeval timeout = {5, 0}; + amqp_rpc_reply_t rpc_reply = + amqp_consume_message(connection_state_, &envelope, &timeout, 0); + assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL); + + *out_body_size_ = envelope.message.body.len; + char *body = malloc(*out_body_size_); + if (*out_body_size_) { + memcpy(body, envelope.message.body.bytes, *out_body_size_); + } + + amqp_destroy_envelope(&envelope); + return body; +} + +void publish_and_consume_message(const char *msg_to_publish) { + amqp_connection_state_t connection_state = setup_connection_and_channel(); + + queue_declare(connection_state, test_queue_name); + basic_publish(connection_state, msg_to_publish); + + uint64_t body_size; + char *msg = consume_message(connection_state, test_queue_name, &body_size); + + assert(body_size == strlen(msg_to_publish)); + assert(strncmp(msg_to_publish, msg, body_size) == 0); + free(msg); + + close_and_destroy_connection(connection_state); +} + +int main(void) { + publish_and_basic_get_message(""); + publish_and_basic_get_message("TEST"); + + publish_and_consume_message(""); + publish_and_consume_message("TEST"); + + return 0; +} diff --git a/ext/librabbitmq/tests/test_hostcheck.c b/ext/librabbitmq/tests/test_hostcheck.c new file mode 100644 index 000000000..24c0d6c28 --- /dev/null +++ b/ext/librabbitmq/tests/test_hostcheck.c @@ -0,0 +1,71 @@ +/* + * Copyright 2014 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp_hostcheck.h" + +#include +#include + +static void hostcheck_success(const char *match_pattern, const char *url) { + int ok; + + ok = amqp_hostcheck(match_pattern, url); + if (!ok) { + fprintf(stderr, "Expected hostname check to pass, but didn't: %s (%s)\n", + url, match_pattern); + abort(); + } + + fprintf(stdout, "ok: [success] %s, %s\n", url, match_pattern); +} + +static void hostcheck_fail(const char *match_pattern, const char *url) { + int ok; + + ok = amqp_hostcheck(match_pattern, url); + if (ok) { + fprintf(stderr, "Expected hostname check to fail, but didn't: %s (%s)\n", + url, match_pattern); + abort(); + } + + fprintf(stdout, "ok: [fail] %s, %s\n", url, match_pattern); +} + +int main(void) { + hostcheck_success("www.rabbitmq.com", "www.rabbitmq.com"); + hostcheck_success("www.rabbitmq.com", "wWw.RaBbItMq.CoM"); + hostcheck_success("*.rabbitmq.com", "wWw.RaBbItMq.CoM"); + hostcheck_fail("rabbitmq.com", "www.rabbitmq.com"); + hostcheck_success("*.rabbitmq.com", "www.rabbitmq.com"); + hostcheck_fail("*.com", "www.rabbitmq.com"); + hostcheck_fail("*.rabbitmq.com", "long.url.rabbitmq.com"); + hostcheck_success("*.url.rabbitmq.com", "long.url.rabbitmq.com"); + + return 0; +} diff --git a/ext/librabbitmq/tests/test_merge_capabilities.c b/ext/librabbitmq/tests/test_merge_capabilities.c new file mode 100644 index 000000000..d62fcd316 --- /dev/null +++ b/ext/librabbitmq/tests/test_merge_capabilities.c @@ -0,0 +1,203 @@ +/* + * Copyright 2015 Alan Antonuk. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "amqp_socket.h" +#include "amqp_table.h" + +#include +#include + +static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r); +static int compare_amqp_table_entry(amqp_table_entry_t result, + amqp_table_entry_t expect); +static int compare_field_value(amqp_field_value_t result, + amqp_field_value_t expect); +static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect); + +static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r) { + if (l.len == r.len && + (l.bytes == r.bytes || 0 == memcmp(l.bytes, r.bytes, l.len))) { + return 1; + } + return 0; +} + +static int compare_amqp_table_entry(amqp_table_entry_t result, + amqp_table_entry_t expect) { + if (!compare_bytes(result.key, expect.key)) { + return 0; + } + return compare_field_value(result.value, expect.value); +} + +static int compare_field_value(amqp_field_value_t result, + amqp_field_value_t expect) { + if (result.kind != expect.kind) { + return 0; + } + switch (result.kind) { + case AMQP_FIELD_KIND_BOOLEAN: + return result.value.boolean == expect.value.boolean; + case AMQP_FIELD_KIND_I8: + return result.value.i8 == expect.value.i8; + case AMQP_FIELD_KIND_U8: + return result.value.u8 == expect.value.u8; + case AMQP_FIELD_KIND_I16: + return result.value.i16 == expect.value.i16; + case AMQP_FIELD_KIND_U16: + return result.value.u16 == expect.value.u16; + case AMQP_FIELD_KIND_I32: + return result.value.i32 == expect.value.i32; + case AMQP_FIELD_KIND_U32: + return result.value.u32 == expect.value.u32; + case AMQP_FIELD_KIND_I64: + return result.value.i64 == expect.value.i64; + case AMQP_FIELD_KIND_U64: + case AMQP_FIELD_KIND_TIMESTAMP: + return result.value.u64 == expect.value.u64; + case AMQP_FIELD_KIND_F32: + return result.value.f32 == expect.value.f32; + case AMQP_FIELD_KIND_F64: + return result.value.f64 == expect.value.f64; + case AMQP_FIELD_KIND_DECIMAL: + return !memcmp(&result.value.decimal, &expect.value.decimal, + sizeof(expect.value.decimal)); + case AMQP_FIELD_KIND_UTF8: + case AMQP_FIELD_KIND_BYTES: + return compare_bytes(result.value.bytes, expect.value.bytes); + case AMQP_FIELD_KIND_ARRAY: { + int i; + if (result.value.array.num_entries != expect.value.array.num_entries) { + return 0; + } + for (i = 0; i < result.value.array.num_entries; ++i) { + if (!compare_field_value(result.value.array.entries[i], + expect.value.array.entries[i])) { + return 0; + } + } + return 1; + } + case AMQP_FIELD_KIND_TABLE: + return compare_amqp_table(&result.value.table, &expect.value.table); + case AMQP_FIELD_KIND_VOID: + return 1; + } + return 1; +} + +static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect) { + int i; + + if (result->num_entries != expect->num_entries) { + return 0; + } + + for (i = 0; i < expect->num_entries; ++i) { + if (!compare_amqp_table_entry(expect->entries[i], result->entries[i])) { + return 0; + } + } + return 1; +} + +static void test_merge_capabilities(amqp_table_t* base, amqp_table_t* add, + amqp_table_t* expect) { + amqp_pool_t pool; + amqp_table_t result; + int res; + init_amqp_pool(&pool, 4096); + + res = amqp_merge_capabilities(base, add, &result, &pool); + if (AMQP_STATUS_OK != res) { + fprintf(stderr, "amqp_merge_capabilities returned !ok: %d\n", res); + abort(); + } + + if (!compare_amqp_table(&result, expect)) { + fprintf(stderr, "amqp_merge_capabilities incorrect result.\n"); + abort(); + } + empty_amqp_pool(&pool); + return; +} + +int main(void) { + { + amqp_table_t sub_base; + amqp_table_t sub_add; + amqp_table_t sub_expect; + amqp_table_t base; + amqp_table_t add; + amqp_table_t expect; + + amqp_table_entry_t sub_base_entries[1]; + amqp_table_entry_t sub_add_entries[2]; + amqp_table_entry_t sub_expect_entries[2]; + + amqp_table_entry_t base_entries[3]; + amqp_table_entry_t add_entries[3]; + amqp_table_entry_t expect_entries[4]; + + sub_base_entries[0] = amqp_table_construct_utf8_entry("foo", "bar"); + sub_base.num_entries = + sizeof(sub_base_entries) / sizeof(amqp_table_entry_t); + sub_base.entries = sub_base_entries; + + sub_add_entries[0] = amqp_table_construct_utf8_entry("something", "else"); + sub_add_entries[1] = amqp_table_construct_utf8_entry("foo", "baz"); + sub_add.num_entries = sizeof(sub_add_entries) / sizeof(amqp_table_entry_t); + sub_add.entries = sub_add_entries; + + sub_expect_entries[0] = amqp_table_construct_utf8_entry("foo", "baz"); + sub_expect_entries[1] = + amqp_table_construct_utf8_entry("something", "else"); + sub_expect.num_entries = + sizeof(sub_expect_entries) / sizeof(amqp_table_entry_t); + sub_expect.entries = sub_expect_entries; + + base_entries[0] = amqp_table_construct_utf8_entry("product", "1.0"); + base_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah"); + base_entries[2] = amqp_table_construct_table_entry("props", &sub_base); + base.num_entries = sizeof(base_entries) / sizeof(amqp_table_entry_t); + base.entries = base_entries; + + add_entries[0] = amqp_table_construct_bool_entry("bool_entry", 1); + add_entries[1] = amqp_table_construct_utf8_entry("product", "2.0"); + add_entries[2] = amqp_table_construct_table_entry("props", &sub_add); + add.num_entries = sizeof(add_entries) / sizeof(amqp_table_entry_t); + add.entries = add_entries; + + expect_entries[0] = amqp_table_construct_utf8_entry("product", "2.0"), + expect_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah"), + expect_entries[2] = amqp_table_construct_table_entry("props", &sub_expect); + expect_entries[3] = amqp_table_construct_bool_entry("bool_entry", 1); + expect.num_entries = sizeof(expect_entries) / sizeof(amqp_table_entry_t); + expect.entries = expect_entries; + + test_merge_capabilities(&base, &add, &expect); + } + fprintf(stderr, "ok\n"); + return 0; +} diff --git a/ext/librabbitmq/tests/test_parse_url.c b/ext/librabbitmq/tests/test_parse_url.c new file mode 100644 index 000000000..9cdc87c10 --- /dev/null +++ b/ext/librabbitmq/tests/test_parse_url.c @@ -0,0 +1,220 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include "config.h" + +#ifdef _MSC_VER +/* MSVC complains about strdup being deprecated in favor of _strdup */ +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include +#include +#include + +#include + +#include + +static void match_string(const char *what, const char *expect, + const char *got) { + if (strcmp(got, expect)) { + fprintf(stderr, "Expected %s '%s', got '%s'\n", what, expect, got); + abort(); + } +} + +static void match_int(const char *what, int expect, int got) { + if (got != expect) { + fprintf(stderr, "Expected %s '%d', got '%d'\n", what, expect, got); + abort(); + } +} + +static void parse_success(const char *url, const char *user, + const char *password, const char *host, int port, + const char *vhost) { + char *s = strdup(url); + struct amqp_connection_info ci; + int res; + + res = amqp_parse_url(s, &ci); + if (res) { + fprintf(stderr, "Expected to successfully parse URL, but didn't: %s (%s)\n", + url, amqp_error_string2(res)); + abort(); + } + + match_string("user", user, ci.user); + match_string("password", password, ci.password); + match_string("host", host, ci.host); + match_int("port", port, ci.port); + match_string("vhost", vhost, ci.vhost); + + free(s); +} + +static void parse_fail(const char *url) { + char *s = strdup(url); + struct amqp_connection_info ci; + + amqp_default_connection_info(&ci); + if (amqp_parse_url(s, &ci) >= 0) { + fprintf(stderr, "Expected to fail parsing URL, but didn't: %s\n", url); + abort(); + } + + free(s); +} + +int main(void) { + /* From the spec */ + parse_success("amqp://user:pass@host:10000/vhost", "user", "pass", "host", + 10000, "vhost"); + parse_success("amqps://user:pass@host:10000/vhost", "user", "pass", "host", + 10000, "vhost"); + + parse_success("amqp://user%61:%61pass@ho%61st:10000/v%2fhost", "usera", + "apass", "hoast", 10000, "v/host"); + parse_success("amqps://user%61:%61pass@ho%61st:10000/v%2fhost", "usera", + "apass", "hoast", 10000, "v/host"); + + parse_success("amqp://", "guest", "guest", "localhost", 5672, "/"); + parse_success("amqps://", "guest", "guest", "localhost", 5671, "/"); + + parse_success("amqp://:@/", "", "", "localhost", 5672, ""); + parse_success("amqps://:@/", "", "", "localhost", 5671, ""); + + parse_success("amqp://user@", "user", "guest", "localhost", 5672, "/"); + parse_success("amqps://user@", "user", "guest", "localhost", 5671, "/"); + + parse_success("amqp://user:pass@", "user", "pass", "localhost", 5672, "/"); + parse_success("amqps://user:pass@", "user", "pass", "localhost", 5671, "/"); + + parse_success("amqp://host", "guest", "guest", "host", 5672, "/"); + parse_success("amqps://host", "guest", "guest", "host", 5671, "/"); + + parse_success("amqp://:10000", "guest", "guest", "localhost", 10000, "/"); + parse_success("amqps://:10000", "guest", "guest", "localhost", 10000, "/"); + + parse_success("amqp:///vhost", "guest", "guest", "localhost", 5672, "vhost"); + parse_success("amqps:///vhost", "guest", "guest", "localhost", 5671, "vhost"); + + parse_success("amqp://host/", "guest", "guest", "host", 5672, ""); + parse_success("amqps://host/", "guest", "guest", "host", 5671, ""); + + parse_success("amqp://host/%2f", "guest", "guest", "host", 5672, "/"); + parse_success("amqps://host/%2f", "guest", "guest", "host", 5671, "/"); + + parse_success("amqp://[::1]", "guest", "guest", "::1", 5672, "/"); + parse_success("amqps://[::1]", "guest", "guest", "::1", 5671, "/"); + + /* Various other success cases */ + parse_success("amqp://host:100", "guest", "guest", "host", 100, "/"); + parse_success("amqps://host:100", "guest", "guest", "host", 100, "/"); + + parse_success("amqp://[::1]:100", "guest", "guest", "::1", 100, "/"); + parse_success("amqps://[::1]:100", "guest", "guest", "::1", 100, "/"); + + parse_success("amqp://host/blah", "guest", "guest", "host", 5672, "blah"); + parse_success("amqps://host/blah", "guest", "guest", "host", 5671, "blah"); + + parse_success("amqp://host:100/blah", "guest", "guest", "host", 100, "blah"); + parse_success("amqps://host:100/blah", "guest", "guest", "host", 100, "blah"); + + parse_success("amqp://:100/blah", "guest", "guest", "localhost", 100, "blah"); + parse_success("amqps://:100/blah", "guest", "guest", "localhost", 100, + "blah"); + + parse_success("amqp://[::1]/blah", "guest", "guest", "::1", 5672, "blah"); + parse_success("amqps://[::1]/blah", "guest", "guest", "::1", 5671, "blah"); + + parse_success("amqp://[::1]:100/blah", "guest", "guest", "::1", 100, "blah"); + parse_success("amqps://[::1]:100/blah", "guest", "guest", "::1", 100, "blah"); + + parse_success("amqp://user:pass@host", "user", "pass", "host", 5672, "/"); + parse_success("amqps://user:pass@host", "user", "pass", "host", 5671, "/"); + + parse_success("amqp://user:pass@host:100", "user", "pass", "host", 100, "/"); + parse_success("amqps://user:pass@host:100", "user", "pass", "host", 100, "/"); + + parse_success("amqp://user:pass@:100", "user", "pass", "localhost", 100, "/"); + parse_success("amqps://user:pass@:100", "user", "pass", "localhost", 100, + "/"); + + parse_success("amqp://user:pass@[::1]", "user", "pass", "::1", 5672, "/"); + parse_success("amqps://user:pass@[::1]", "user", "pass", "::1", 5671, "/"); + + parse_success("amqp://user:pass@[::1]:100", "user", "pass", "::1", 100, "/"); + parse_success("amqps://user:pass@[::1]:100", "user", "pass", "::1", 100, "/"); + + /* Various failure cases */ + parse_fail("http://www.rabbitmq.com"); + + parse_fail("amqp://foo:bar:baz"); + parse_fail("amqps://foo:bar:baz"); + + parse_fail("amqp://foo[::1]"); + parse_fail("amqps://foo[::1]"); + + parse_fail("amqp://foo[::1]"); + parse_fail("amqps://foo[::1]"); + + parse_fail("amqp://foo:[::1]"); + parse_fail("amqps://foo:[::1]"); + + parse_fail("amqp://[::1]foo"); + parse_fail("amqps://[::1]foo"); + + parse_fail("amqp://foo:1000xyz"); + parse_fail("amqps://foo:1000xyz"); + + parse_fail("amqp://foo:1000000"); + parse_fail("amqps://foo:1000000"); + + parse_fail("amqp://foo/bar/baz"); + parse_fail("amqps://foo/bar/baz"); + + parse_fail("amqp://foo%1"); + parse_fail("amqps://foo%1"); + + parse_fail("amqp://foo%1x"); + parse_fail("amqps://foo%1x"); + + parse_fail("amqp://foo%xy"); + parse_fail("amqps://foo%xy"); + + return 0; +} diff --git a/ext/librabbitmq/tests/test_sasl_mechanism.c b/ext/librabbitmq/tests/test_sasl_mechanism.c new file mode 100644 index 000000000..78482198f --- /dev/null +++ b/ext/librabbitmq/tests/test_sasl_mechanism.c @@ -0,0 +1,70 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include + +static void parse_success(amqp_bytes_t mechanisms, + amqp_sasl_method_enum method) { + if (!sasl_mechanism_in_list(mechanisms, method)) { + fprintf(stderr, "Expected to find mechanism in list, but didn't: %s\n", + (char *)mechanisms.bytes); + abort(); + } +} + +static void parse_fail(amqp_bytes_t mechanisms, amqp_sasl_method_enum method) { + if (sasl_mechanism_in_list(mechanisms, method)) { + fprintf(stderr, + "Expected the mechanism not on the list, but it was present: %s\n", + (char *)mechanisms.bytes); + abort(); + } +} + +int main(void) { + parse_success(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"), + AMQP_SASL_METHOD_PLAIN); + parse_fail(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"), + AMQP_SASL_METHOD_EXTERNAL); + parse_success(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"), + AMQP_SASL_METHOD_EXTERNAL); + parse_fail(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"), + AMQP_SASL_METHOD_PLAIN); + return 0; +} diff --git a/ext/librabbitmq/tests/test_status_enum.c b/ext/librabbitmq/tests/test_status_enum.c new file mode 100644 index 000000000..2f2dbd7f6 --- /dev/null +++ b/ext/librabbitmq/tests/test_status_enum.c @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Alan Antonuk + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amqp.h" + +#include +#include +#include + +static void check_errorstrings(amqp_status_enum start, amqp_status_enum end) { + int i; + for (i = start; i > end; --i) { + const char* err = amqp_error_string2(i); + if (0 == strcmp(err, "(unknown error)")) { + printf("amqp_status_enum value %s%X", i < 0 ? "-" : "", (unsigned)i); + abort(); + } + } +} + +int main(void) { + check_errorstrings(AMQP_STATUS_OK, _AMQP_STATUS_NEXT_VALUE); + check_errorstrings(AMQP_STATUS_TCP_ERROR, _AMQP_STATUS_TCP_NEXT_VALUE); + check_errorstrings(AMQP_STATUS_SSL_ERROR, _AMQP_STATUS_SSL_NEXT_VALUE); + + return 0; +} diff --git a/ext/librabbitmq/tests/test_tables.c b/ext/librabbitmq/tests/test_tables.c new file mode 100644 index 000000000..89ece6b47 --- /dev/null +++ b/ext/librabbitmq/tests/test_tables.c @@ -0,0 +1,466 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +void die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + abort(); +} + +static void dump_indent(int indent, FILE *out) { + int i; + + for (i = 0; i < indent; i++) { + fputc(' ', out); + } +} + +static void dump_value(int indent, amqp_field_value_t v, FILE *out) { + int i; + + dump_indent(indent, out); + fputc(v.kind, out); + + switch (v.kind) { + case AMQP_FIELD_KIND_BOOLEAN: + fputs(v.value.boolean ? " true\n" : " false\n", out); + break; + + case AMQP_FIELD_KIND_I8: + fprintf(out, " %" PRId8 "\n", v.value.i8); + break; + + case AMQP_FIELD_KIND_U8: + fprintf(out, " %" PRIu8 "\n", v.value.u8); + break; + + case AMQP_FIELD_KIND_I16: + fprintf(out, " %" PRId16 "\n", v.value.i16); + break; + + case AMQP_FIELD_KIND_U16: + fprintf(out, " %" PRIu16 "\n", v.value.u16); + break; + + case AMQP_FIELD_KIND_I32: + fprintf(out, " %" PRId32 "\n", v.value.i32); + break; + + case AMQP_FIELD_KIND_U32: + fprintf(out, " %" PRIu32 "\n", v.value.u32); + break; + + case AMQP_FIELD_KIND_I64: + fprintf(out, " %" PRId64 "\n", v.value.i64); + break; + + case AMQP_FIELD_KIND_F32: + fprintf(out, " %g\n", (double)v.value.f32); + break; + + case AMQP_FIELD_KIND_F64: + fprintf(out, " %g\n", v.value.f64); + break; + + case AMQP_FIELD_KIND_DECIMAL: + fprintf(out, " %u:::%u\n", v.value.decimal.decimals, + v.value.decimal.value); + break; + + case AMQP_FIELD_KIND_UTF8: + fprintf(out, " %.*s\n", (int)v.value.bytes.len, + (char *)v.value.bytes.bytes); + break; + + case AMQP_FIELD_KIND_BYTES: + fputc(' ', out); + for (i = 0; i < (int)v.value.bytes.len; i++) { + fprintf(out, "%02x", ((char *)v.value.bytes.bytes)[i]); + } + + fputc('\n', out); + break; + + case AMQP_FIELD_KIND_ARRAY: + fputc('\n', out); + for (i = 0; i < v.value.array.num_entries; i++) { + dump_value(indent + 2, v.value.array.entries[i], out); + } + + break; + + case AMQP_FIELD_KIND_TIMESTAMP: + fprintf(out, " %" PRIu64 "\n", v.value.u64); + break; + + case AMQP_FIELD_KIND_TABLE: + fputc('\n', out); + for (i = 0; i < v.value.table.num_entries; i++) { + dump_indent(indent + 2, out); + fprintf(out, "%.*s ->\n", (int)v.value.table.entries[i].key.len, + (char *)v.value.table.entries[i].key.bytes); + dump_value(indent + 4, v.value.table.entries[i].value, out); + } + + break; + + case AMQP_FIELD_KIND_VOID: + fputc('\n', out); + break; + + default: + fprintf(out, "???\n"); + break; + } +} + +static void test_dump_value(FILE *out) { + amqp_table_entry_t entries[8]; + amqp_table_t table; + amqp_field_value_t val; + + entries[0].key = amqp_cstring_bytes("zebra"); + entries[0].value.kind = AMQP_FIELD_KIND_UTF8; + entries[0].value.value.bytes = amqp_cstring_bytes("last"); + + entries[1].key = amqp_cstring_bytes("aardvark"); + entries[1].value.kind = AMQP_FIELD_KIND_UTF8; + entries[1].value.value.bytes = amqp_cstring_bytes("first"); + + entries[2].key = amqp_cstring_bytes("middle"); + entries[2].value.kind = AMQP_FIELD_KIND_UTF8; + entries[2].value.value.bytes = amqp_cstring_bytes("third"); + + entries[3].key = amqp_cstring_bytes("number"); + entries[3].value.kind = AMQP_FIELD_KIND_I32; + entries[3].value.value.i32 = 1234; + + entries[4].key = amqp_cstring_bytes("decimal"); + entries[4].value.kind = AMQP_FIELD_KIND_DECIMAL; + entries[4].value.value.decimal.decimals = 2; + entries[4].value.value.decimal.value = 1234; + + entries[5].key = amqp_cstring_bytes("time"); + entries[5].value.kind = AMQP_FIELD_KIND_TIMESTAMP; + entries[5].value.value.u64 = 1234123412341234; + + entries[6].key = amqp_cstring_bytes("beta"); + entries[6].value.kind = AMQP_FIELD_KIND_UTF8; + entries[6].value.value.bytes = amqp_cstring_bytes("second"); + + entries[7].key = amqp_cstring_bytes("wombat"); + entries[7].value.kind = AMQP_FIELD_KIND_UTF8; + entries[7].value.value.bytes = amqp_cstring_bytes("fourth"); + + table.num_entries = 8; + table.entries = entries; + + qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t), + &amqp_table_entry_cmp); + + val.kind = AMQP_FIELD_KIND_TABLE; + val.value.table = table; + + dump_value(0, val, out); +} + +static uint8_t pre_encoded_table[] = { + 0x00, 0x00, 0x00, 0xff, 0x07, 0x6c, 0x6f, 0x6e, 0x67, 0x73, 0x74, 0x72, + 0x53, 0x00, 0x00, 0x00, 0x15, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x69, 0x6e, 0x74, + 0x49, 0x00, 0x00, 0x30, 0x39, 0x07, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, + 0x6c, 0x44, 0x03, 0x00, 0x01, 0xe2, 0x40, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x54, 0x00, 0x00, 0x63, 0xee, 0xa0, 0x53, + 0xc1, 0x94, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x00, 0x00, 0x00, + 0x1f, 0x03, 0x6f, 0x6e, 0x65, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x03, 0x74, + 0x77, 0x6f, 0x53, 0x00, 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e, + 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x04, 0x62, 0x79, 0x74, + 0x65, 0x62, 0xff, 0x04, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x96, 0x02, 0xd2, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x73, + 0x02, 0x8f, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x74, 0x01, 0x06, 0x62, 0x69, + 0x6e, 0x61, 0x72, 0x79, 0x78, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x20, 0x62, + 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x04, 0x76, 0x6f, 0x69, 0x64, 0x56, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x41, 0x00, 0x00, 0x00, 0x17, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x53, 0x00, + 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x66, 0x40, + 0x49, 0x0f, 0xdb, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x64, 0x40, + 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18}; + +static void test_table_codec(FILE *out) { + amqp_pool_t pool; + int result; + + amqp_table_entry_t inner_entries[2]; + amqp_table_t inner_table; + + amqp_field_value_t inner_values[2]; + amqp_array_t inner_array; + + amqp_table_entry_t entries[14]; + amqp_table_t table; + + inner_entries[0].key = amqp_cstring_bytes("one"); + inner_entries[0].value.kind = AMQP_FIELD_KIND_I32; + inner_entries[0].value.value.i32 = 54321; + + inner_entries[1].key = amqp_cstring_bytes("two"); + inner_entries[1].value.kind = AMQP_FIELD_KIND_UTF8; + inner_entries[1].value.value.bytes = amqp_cstring_bytes("A long string"); + + inner_table.num_entries = 2; + inner_table.entries = inner_entries; + + inner_values[0].kind = AMQP_FIELD_KIND_I32; + inner_values[0].value.i32 = 54321; + + inner_values[1].kind = AMQP_FIELD_KIND_UTF8; + inner_values[1].value.bytes = amqp_cstring_bytes("A long string"); + + inner_array.num_entries = 2; + inner_array.entries = inner_values; + + entries[0].key = amqp_cstring_bytes("longstr"); + entries[0].value.kind = AMQP_FIELD_KIND_UTF8; + entries[0].value.value.bytes = amqp_cstring_bytes("Here is a long string"); + + entries[1].key = amqp_cstring_bytes("signedint"); + entries[1].value.kind = AMQP_FIELD_KIND_I32; + entries[1].value.value.i32 = 12345; + + entries[2].key = amqp_cstring_bytes("decimal"); + entries[2].value.kind = AMQP_FIELD_KIND_DECIMAL; + entries[2].value.value.decimal.decimals = 3; + entries[2].value.value.decimal.value = 123456; + + entries[3].key = amqp_cstring_bytes("timestamp"); + entries[3].value.kind = AMQP_FIELD_KIND_TIMESTAMP; + entries[3].value.value.u64 = 109876543209876; + + entries[4].key = amqp_cstring_bytes("table"); + entries[4].value.kind = AMQP_FIELD_KIND_TABLE; + entries[4].value.value.table = inner_table; + + entries[5].key = amqp_cstring_bytes("byte"); + entries[5].value.kind = AMQP_FIELD_KIND_I8; + entries[5].value.value.i8 = (int8_t)-1; + + entries[6].key = amqp_cstring_bytes("long"); + entries[6].value.kind = AMQP_FIELD_KIND_I64; + entries[6].value.value.i64 = 1234567890; + + entries[7].key = amqp_cstring_bytes("short"); + entries[7].value.kind = AMQP_FIELD_KIND_I16; + entries[7].value.value.i16 = 655; + + entries[8].key = amqp_cstring_bytes("bool"); + entries[8].value.kind = AMQP_FIELD_KIND_BOOLEAN; + entries[8].value.value.boolean = 1; + + entries[9].key = amqp_cstring_bytes("binary"); + entries[9].value.kind = AMQP_FIELD_KIND_BYTES; + entries[9].value.value.bytes = amqp_cstring_bytes("a binary string"); + + entries[10].key = amqp_cstring_bytes("void"); + entries[10].value.kind = AMQP_FIELD_KIND_VOID; + + entries[11].key = amqp_cstring_bytes("array"); + entries[11].value.kind = AMQP_FIELD_KIND_ARRAY; + entries[11].value.value.array = inner_array; + + entries[12].key = amqp_cstring_bytes("float"); + entries[12].value.kind = AMQP_FIELD_KIND_F32; + entries[12].value.value.f32 = (float)M_PI; + + entries[13].key = amqp_cstring_bytes("double"); + entries[13].value.kind = AMQP_FIELD_KIND_F64; + entries[13].value.value.f64 = M_PI; + + table.num_entries = 14; + table.entries = entries; + + fprintf(out, "AAAAAAAAAA\n"); + + { + amqp_field_value_t val; + val.kind = AMQP_FIELD_KIND_TABLE; + val.value.table = table; + dump_value(0, val, out); + } + + init_amqp_pool(&pool, 4096); + + { + amqp_table_t decoded; + size_t decoding_offset = 0; + amqp_bytes_t decoding_bytes; + decoding_bytes.len = sizeof(pre_encoded_table); + decoding_bytes.bytes = pre_encoded_table; + + result = + amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset); + if (result < 0) { + die("Table decoding failed: %s", amqp_error_string2(result)); + } + + fprintf(out, "BBBBBBBBBB\n"); + + { + amqp_field_value_t val; + val.kind = AMQP_FIELD_KIND_TABLE; + val.value.table = decoded; + + dump_value(0, val, out); + } + } + + { + uint8_t encoding_buffer[4096]; + amqp_bytes_t encoding_result; + size_t offset = 0; + + memset(&encoding_buffer[0], 0, sizeof(encoding_buffer)); + encoding_result.len = sizeof(encoding_buffer); + encoding_result.bytes = &encoding_buffer[0]; + + result = amqp_encode_table(encoding_result, &table, &offset); + if (result < 0) { + die("Table encoding failed: %s", amqp_error_string2(result)); + } + + if (offset != sizeof(pre_encoded_table)) + die("Offset should be %ld, was %ld", (long)sizeof(pre_encoded_table), + (long)offset); + + result = memcmp(pre_encoded_table, encoding_buffer, offset); + if (result != 0) { + die("Table encoding differed", result); + } + } + + empty_amqp_pool(&pool); +} + +#define CHUNK_SIZE 4096 + +static int compare_files(FILE *f1_in, FILE *f2_in) { + char f1_buf[CHUNK_SIZE]; + char f2_buf[CHUNK_SIZE]; + int res; + + rewind(f1_in); + rewind(f2_in); + + for (;;) { + size_t f1_got = fread(f1_buf, 1, CHUNK_SIZE, f1_in); + size_t f2_got = fread(f2_buf, 1, CHUNK_SIZE, f2_in); + res = memcmp(f1_buf, f2_buf, f1_got < f2_got ? f1_got : f2_got); + + if (res) { + break; + } + + if (f1_got < CHUNK_SIZE || f2_got < CHUNK_SIZE) { + if (f1_got != f2_got) { + res = (f1_got < f2_got ? -1 : 1); + } + break; + } + } + + return res; +} + +const char *expected_file_name = "tests/test_tables.expected"; + +int main(void) { + char *srcdir = getenv("srcdir"); + FILE *out, *expected = NULL; + char *expected_path; + + out = tmpfile(); + if (out == NULL) { + die("failed to create temporary file: %s", strerror(errno)); + } + + test_table_codec(out); + fprintf(out, "----------\n"); + test_dump_value(out); + + if (srcdir == NULL) { + srcdir = "."; + } + + expected_path = malloc(strlen(srcdir) + strlen(expected_file_name) + 2); + if (!expected_path) { + die("out of memory"); + } + sprintf(expected_path, "%s/%s", srcdir, expected_file_name); + expected = fopen(expected_path, "r"); + if (!expected) { + die("failed to open %s: %s", expected_path, strerror(errno)); + } + + if (compare_files(expected, out)) { + die("output file did not have expected contents"); + } + + fclose(expected); + free(expected_path); + fclose(out); + + return 0; +} diff --git a/ext/librabbitmq/tests/test_tables.expected b/ext/librabbitmq/tests/test_tables.expected new file mode 100644 index 000000000..44d208528 --- /dev/null +++ b/ext/librabbitmq/tests/test_tables.expected @@ -0,0 +1,90 @@ +AAAAAAAAAA +F + longstr -> + S Here is a long string + signedint -> + I 12345 + decimal -> + D 3:::123456 + timestamp -> + T 109876543209876 + table -> + F + one -> + I 54321 + two -> + S A long string + byte -> + b -1 + long -> + l 1234567890 + short -> + s 655 + bool -> + t true + binary -> + x 612062696e61727920737472696e67 + void -> + V + array -> + A + I 54321 + S A long string + float -> + f 3.14159 + double -> + d 3.14159 +BBBBBBBBBB +F + longstr -> + S Here is a long string + signedint -> + I 12345 + decimal -> + D 3:::123456 + timestamp -> + T 109876543209876 + table -> + F + one -> + I 54321 + two -> + S A long string + byte -> + b -1 + long -> + l 1234567890 + short -> + s 655 + bool -> + t true + binary -> + x 612062696e61727920737472696e67 + void -> + V + array -> + A + I 54321 + S A long string + float -> + f 3.14159 + double -> + d 3.14159 +---------- +F + aardvark -> + S first + beta -> + S second + decimal -> + D 2:::1234 + middle -> + S third + number -> + I 1234 + time -> + T 1234123412341234 + wombat -> + S fourth + zebra -> + S last diff --git a/ext/librabbitmq/tests/win32/msinttypes/inttypes.h b/ext/librabbitmq/tests/win32/msinttypes/inttypes.h new file mode 100644 index 000000000..f437cf45a --- /dev/null +++ b/ext/librabbitmq/tests/win32/msinttypes/inttypes.h @@ -0,0 +1,304 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || \ + defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +#define SCNdPTR "I64d" +#define SCNiPTR "I64i" +#else // _WIN64 ][ +#define SCNdPTR "ld" +#define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +#define SCNoPTR "I64o" +#define SCNuPTR "I64u" +#define SCNxPTR "I64x" +#define SCNXPTR "I64X" +#else // _WIN64 ][ +#define SCNoPTR "lo" +#define SCNuPTR "lu" +#define SCNxPTR "lx" +#define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] + imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/ext/librabbitmq/tools/CMakeLists.txt b/ext/librabbitmq/tools/CMakeLists.txt new file mode 100644 index 000000000..1471043c4 --- /dev/null +++ b/ext/librabbitmq/tools/CMakeLists.txt @@ -0,0 +1,82 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LIBRABBITMQ_INCLUDE_DIRS} ${POPT_INCLUDE_DIR}) + +if (WIN32) + set(PLATFORM_DIR win32) + set(PLATFORM_SRCS + win32/compat.c + ) +else (WIN32) + set(PLATFORM_DIR unix) +endif (WIN32) + +include_directories(${PLATFORM_DIR}) + +set(COMMON_SRCS + common.h + common.c + ${PLATFORM_SRCS} + ) + +add_executable(amqp-publish publish.c ${COMMON_SRCS}) +target_link_libraries(amqp-publish ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY}) + +add_executable(amqp-get get.c ${COMMON_SRCS}) +target_link_libraries(amqp-get ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY}) + +add_executable(amqp-consume consume.c ${PLATFORM_DIR}/process.c ${COMMON_SRCS}) +target_link_libraries(amqp-consume ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY}) + +add_executable(amqp-declare-queue declare_queue.c ${COMMON_SRCS}) +target_link_libraries(amqp-declare-queue ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY}) + +add_executable(amqp-delete-queue delete_queue.c ${COMMON_SRCS}) +target_link_libraries(amqp-delete-queue ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY}) + +if (BUILD_TOOLS_DOCS) + if (XMLTO_FOUND) + set(DOCS_SRCS + doc/amqp-consume.xml + doc/amqp-declare-queue.xml + doc/amqp-delete-queue.xml + doc/amqp-get.xml + doc/amqp-publish.xml + doc/librabbitmq-tools.xml + ) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc) + set(XMLTO_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doc/man-date.ent) + add_custom_command( + OUTPUT ${XMLTO_DEPENDS} + COMMAND date +'%Y-%m-%d' > ${XMLTO_DEPENDS} + VERBATIM + ) + + set(XMLTO_COMMAND_ARGS --skip-validation --searchpath "${CMAKE_CURRENT_BINARY_DIR}/doc") + + XMLTO(${DOCS_SRCS} + MODES man + ALL) + + foreach(file ${XMLTO_FILES_man}) + get_filename_component(fileExt ${file} EXT) + string( REGEX REPLACE "^[.]" "" fileExt ${fileExt} ) + install( + FILES ${file} + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man${fileExt} + ) + endforeach() + + else(XMLTO_FOUND) + message(WARNING "xmlto not found, will not build tools documentation") + endif(XMLTO_FOUND) +endif() + +if (ENABLE_SSL_SUPPORT) + add_definitions(-DWITH_SSL=1) +endif() + +install(TARGETS amqp-publish amqp-get amqp-consume amqp-declare-queue amqp-delete-queue + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include) diff --git a/ext/librabbitmq/tools/common.c b/ext/librabbitmq/tools/common.c new file mode 100644 index 000000000..13839a881 --- /dev/null +++ b/ext/librabbitmq/tools/common.c @@ -0,0 +1,444 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "common.h" +#ifdef WITH_SSL +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WINDOWS +#include "compat.h" +#endif + +void die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +void die_errno(int err, const char *fmt, ...) { + va_list ap; + + if (err == 0) { + return; + } + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(err)); + exit(1); +} + +void die_amqp_error(int err, const char *fmt, ...) { + va_list ap; + + if (err >= 0) { + return; + } + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", amqp_error_string2(err)); + exit(1); +} + +const char *amqp_server_exception_string(amqp_rpc_reply_t r) { + int res; + static char s[512]; + + switch (r.reply.id) { + case AMQP_CONNECTION_CLOSE_METHOD: { + amqp_connection_close_t *m = (amqp_connection_close_t *)r.reply.decoded; + res = snprintf(s, sizeof(s), "server connection error %d, message: %.*s", + m->reply_code, (int)m->reply_text.len, + (char *)m->reply_text.bytes); + break; + } + + case AMQP_CHANNEL_CLOSE_METHOD: { + amqp_channel_close_t *m = (amqp_channel_close_t *)r.reply.decoded; + res = snprintf(s, sizeof(s), "server channel error %d, message: %.*s", + m->reply_code, (int)m->reply_text.len, + (char *)m->reply_text.bytes); + break; + } + + default: + res = snprintf(s, sizeof(s), "unknown server error, method id 0x%08X", + r.reply.id); + break; + } + + return res >= 0 ? s : NULL; +} + +const char *amqp_rpc_reply_string(amqp_rpc_reply_t r) { + switch (r.reply_type) { + case AMQP_RESPONSE_NORMAL: + return "normal response"; + + case AMQP_RESPONSE_NONE: + return "missing RPC reply type"; + + case AMQP_RESPONSE_LIBRARY_EXCEPTION: + return amqp_error_string2(r.library_error); + + case AMQP_RESPONSE_SERVER_EXCEPTION: + return amqp_server_exception_string(r); + + default: + abort(); + } +} + +void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...) { + va_list ap; + + if (r.reply_type == AMQP_RESPONSE_NORMAL) { + return; + } + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", amqp_rpc_reply_string(r)); + exit(1); +} + +static char *amqp_url; +static char *amqp_server; +static int amqp_port = -1; +static char *amqp_vhost; +static char *amqp_username; +static char *amqp_password; +static int amqp_heartbeat = 0; +#ifdef WITH_SSL +static int amqp_ssl = 0; +static char *amqp_cacert = "/etc/ssl/certs/cacert.pem"; +static char *amqp_key = NULL; +static char *amqp_cert = NULL; +#endif /* WITH_SSL */ + +const char *connect_options_title = "Connection options"; +struct poptOption connect_options[] = { + {"url", 'u', POPT_ARG_STRING, &amqp_url, 0, "the AMQP URL to connect to", + "amqp://..."}, + {"server", 's', POPT_ARG_STRING, &amqp_server, 0, + "the AMQP server to connect to", "hostname"}, + {"port", 0, POPT_ARG_INT, &amqp_port, 0, "the port to connect on", "port"}, + {"vhost", 0, POPT_ARG_STRING, &amqp_vhost, 0, + "the vhost to use when connecting", "vhost"}, + {"username", 0, POPT_ARG_STRING, &amqp_username, 0, + "the username to login with", "username"}, + {"password", 0, POPT_ARG_STRING, &amqp_password, 0, + "the password to login with", "password"}, + {"heartbeat", 0, POPT_ARG_INT, &amqp_heartbeat, 0, + "heartbeat interval, set to 0 to disable", "heartbeat"}, +#ifdef WITH_SSL + {"ssl", 0, POPT_ARG_NONE, &amqp_ssl, 0, "connect over SSL/TLS", NULL}, + {"cacert", 0, POPT_ARG_STRING, &amqp_cacert, 0, + "path to the CA certificate file", "cacert.pem"}, + {"key", 0, POPT_ARG_STRING, &amqp_key, 0, + "path to the client private key file", "key.pem"}, + {"cert", 0, POPT_ARG_STRING, &amqp_cert, 0, + "path to the client certificate file", "cert.pem"}, +#endif /* WITH_SSL */ + {NULL, '\0', 0, NULL, 0, NULL, NULL}}; + +static void init_connection_info(struct amqp_connection_info *ci) { + ci->user = NULL; + ci->password = NULL; + ci->host = NULL; + ci->port = -1; + ci->vhost = NULL; + ci->user = NULL; + + amqp_default_connection_info(ci); + + if (amqp_url) + die_amqp_error(amqp_parse_url(strdup(amqp_url), ci), "Parsing URL '%s'", + amqp_url); + + if (amqp_server) { + char *colon; + if (amqp_url) { + die("--server and --url options cannot be used at the same time"); + } + + /* parse the server string into a hostname and a port */ + colon = strchr(amqp_server, ':'); + if (colon) { + char *port_end; + size_t host_len; + + /* Deprecate specifying the port number with the + --server option, because it is not ipv6 friendly. + --url now allows connection options to be + specified concisely. */ + fprintf(stderr, + "Specifying the port number with --server is deprecated\n"); + + host_len = colon - amqp_server; + ci->host = malloc(host_len + 1); + memcpy(ci->host, amqp_server, host_len); + ci->host[host_len] = 0; + + if (amqp_port >= 0) { + die("both --server and --port options specify server port"); + } + + ci->port = strtol(colon + 1, &port_end, 10); + if (ci->port < 0 || ci->port > 65535 || port_end == colon + 1 || + *port_end != 0) + die("bad server port number in '%s'", amqp_server); + } + +#if WITH_SSL + if (amqp_ssl && !ci->ssl) { + die("the --ssl option specifies an SSL connection" + " but the --url option does not"); + } +#endif + } + + if (amqp_port >= 0) { + if (amqp_url) { + die("--port and --url options cannot be used at the same time"); + } + + ci->port = amqp_port; + } + + if (amqp_username) { + if (amqp_url) { + die("--username and --url options cannot be used at the same time"); + } + + ci->user = amqp_username; + } + + if (amqp_password) { + if (amqp_url) { + die("--password and --url options cannot be used at the same time"); + } + + ci->password = amqp_password; + } + + if (amqp_vhost) { + if (amqp_url) { + die("--vhost and --url options cannot be used at the same time"); + } + + ci->vhost = amqp_vhost; + } + + if (amqp_heartbeat < 0) { + die("--heartbeat must be a positive value"); + } +} + +amqp_connection_state_t make_connection(void) { + int status; + amqp_socket_t *socket = NULL; + struct amqp_connection_info ci; + amqp_connection_state_t conn; + + init_connection_info(&ci); + conn = amqp_new_connection(); + if (ci.ssl) { +#ifdef WITH_SSL + socket = amqp_ssl_socket_new(conn); + if (!socket) { + die("creating SSL/TLS socket"); + } + if (amqp_cacert) { + amqp_ssl_socket_set_cacert(socket, amqp_cacert); + } + if (amqp_key) { + amqp_ssl_socket_set_key(socket, amqp_cert, amqp_key); + } +#else + die("librabbitmq was not built with SSL/TLS support"); +#endif + } else { + socket = amqp_tcp_socket_new(conn); + if (!socket) { + die("creating TCP socket (out of memory)"); + } + } + status = amqp_socket_open(socket, ci.host, ci.port); + if (status) { + die("opening socket to %s:%d", ci.host, ci.port); + } + die_rpc(amqp_login(conn, ci.vhost, 0, 131072, amqp_heartbeat, + AMQP_SASL_METHOD_PLAIN, ci.user, ci.password), + "logging in to AMQP server"); + if (!amqp_channel_open(conn, 1)) { + die_rpc(amqp_get_rpc_reply(conn), "opening channel"); + } + return conn; +} + +void close_connection(amqp_connection_state_t conn) { + int res; + die_rpc(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "closing channel"); + die_rpc(amqp_connection_close(conn, AMQP_REPLY_SUCCESS), + "closing connection"); + + res = amqp_destroy_connection(conn); + die_amqp_error(res, "closing connection"); +} + +amqp_bytes_t read_all(int fd) { + size_t space = 4096; + amqp_bytes_t bytes; + + bytes.bytes = malloc(space); + bytes.len = 0; + + for (;;) { + ssize_t res = read(fd, (char *)bytes.bytes + bytes.len, space - bytes.len); + if (res == 0) { + break; + } + + if (res < 0) { + if (errno == EINTR) { + continue; + } + + die_errno(errno, "reading"); + } + + bytes.len += res; + if (bytes.len == space) { + space *= 2; + bytes.bytes = realloc(bytes.bytes, space); + } + } + + return bytes; +} + +void write_all(int fd, amqp_bytes_t data) { + while (data.len > 0) { + ssize_t res = write(fd, data.bytes, data.len); + if (res < 0) { + die_errno(errno, "write"); + } + + data.len -= res; + data.bytes = (char *)data.bytes + res; + } +} + +void copy_body(amqp_connection_state_t conn, int fd) { + size_t body_remaining; + amqp_frame_t frame; + + int res = amqp_simple_wait_frame(conn, &frame); + die_amqp_error(res, "waiting for header frame"); + if (frame.frame_type != AMQP_FRAME_HEADER) { + die("expected header, got frame type 0x%X", frame.frame_type); + } + + body_remaining = frame.payload.properties.body_size; + while (body_remaining) { + res = amqp_simple_wait_frame(conn, &frame); + die_amqp_error(res, "waiting for body frame"); + if (frame.frame_type != AMQP_FRAME_BODY) { + die("expected body, got frame type 0x%X", frame.frame_type); + } + + write_all(fd, frame.payload.body_fragment); + body_remaining -= frame.payload.body_fragment.len; + } +} + +poptContext process_options(int argc, const char **argv, + struct poptOption *options, const char *help) { + int c; + poptContext opts = poptGetContext(NULL, argc, argv, options, 0); + poptSetOtherOptionHelp(opts, help); + + while ((c = poptGetNextOpt(opts)) >= 0) { + /* no options require explicit handling */ + } + + if (c < -1) { + fprintf(stderr, "%s: %s\n", poptBadOption(opts, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + poptPrintUsage(opts, stderr, 0); + exit(1); + } + + return opts; +} + +void process_all_options(int argc, const char **argv, + struct poptOption *options) { + poptContext opts = process_options(argc, argv, options, "[OPTIONS]..."); + const char *opt = poptPeekArg(opts); + + if (opt) { + fprintf(stderr, "unexpected operand: %s\n", opt); + poptPrintUsage(opts, stderr, 0); + exit(1); + } + + poptFreeContext(opts); +} + +amqp_bytes_t cstring_bytes(const char *str) { + return str ? amqp_cstring_bytes(str) : amqp_empty_bytes; +} diff --git a/ext/librabbitmq/tools/common.h b/ext/librabbitmq/tools/common.h new file mode 100644 index 000000000..36b515399 --- /dev/null +++ b/ext/librabbitmq/tools/common.h @@ -0,0 +1,73 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include + +#include + +#include +#include + +extern const char *amqp_server_exception_string(amqp_rpc_reply_t r); +extern const char *amqp_rpc_reply_string(amqp_rpc_reply_t r); + +extern void die(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +extern void die_errno(int err, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +extern void die_amqp_error(int err, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +extern void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +extern const char *connect_options_title; +extern struct poptOption connect_options[]; +extern amqp_connection_state_t make_connection(void); +extern void close_connection(amqp_connection_state_t conn); + +extern amqp_bytes_t read_all(int fd); +extern void write_all(int fd, amqp_bytes_t data); + +extern void copy_body(amqp_connection_state_t conn, int fd); + +#define INCLUDE_OPTIONS(options) \ + { NULL, 0, POPT_ARG_INCLUDE_TABLE, options, 0, options##_title, NULL } + +extern poptContext process_options(int argc, const char **argv, + struct poptOption *options, + const char *help); +extern void process_all_options(int argc, const char **argv, + struct poptOption *options); + +extern amqp_bytes_t cstring_bytes(const char *str); diff --git a/ext/librabbitmq/tools/consume.c b/ext/librabbitmq/tools/consume.c new file mode 100644 index 000000000..dbc164a3d --- /dev/null +++ b/ext/librabbitmq/tools/consume.c @@ -0,0 +1,250 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "common.h" +#include "process.h" + +#define MAX_LISTEN_KEYS 1024 +#define LISTEN_KEYS_DELIMITER "," + +/* Convert a amqp_bytes_t to an escaped string form for printing. We + use the same escaping conventions as rabbitmqctl. */ +static char *stringify_bytes(amqp_bytes_t bytes) { + /* We will need up to 4 chars per byte, plus the terminating 0 */ + char *res = malloc(bytes.len * 4 + 1); + uint8_t *data = bytes.bytes; + char *p = res; + size_t i; + + for (i = 0; i < bytes.len; i++) { + if (data[i] >= 32 && data[i] != 127) { + *p++ = data[i]; + } else { + *p++ = '\\'; + *p++ = '0' + (data[i] >> 6); + *p++ = '0' + (data[i] >> 3 & 0x7); + *p++ = '0' + (data[i] & 0x7); + } + } + + *p = 0; + return res; +} + +static amqp_bytes_t setup_queue(amqp_connection_state_t conn, char *queue, + char *exchange, char *routing_key, int declare, + int exclusive) { + amqp_bytes_t queue_bytes = cstring_bytes(queue); + + char *routing_key_rest; + char *routing_key_token; + char *routing_tmp; + int routing_key_count = 0; + + /* if an exchange name wasn't provided, check that we don't have options that + * require it. */ + if (!exchange && routing_key) { + fprintf(stderr, + "--routing-key option requires an exchange name to be provided " + "with --exchange\n"); + exit(1); + } + + if (!queue || exchange || declare || exclusive) { + /* Declare the queue as auto-delete. */ + amqp_queue_declare_ok_t *res = amqp_queue_declare( + conn, 1, queue_bytes, 0, 0, exclusive, 1, amqp_empty_table); + if (!res) { + die_rpc(amqp_get_rpc_reply(conn), "queue.declare"); + } + + if (!queue) { + /* the server should have provided a queue name */ + char *sq; + queue_bytes = amqp_bytes_malloc_dup(res->queue); + sq = stringify_bytes(queue_bytes); + fprintf(stderr, "Server provided queue name: %s\n", sq); + free(sq); + } + + /* Bind to an exchange if requested */ + if (exchange) { + amqp_bytes_t eb = amqp_cstring_bytes(exchange); + + routing_tmp = strdup(routing_key); + if (NULL == routing_tmp) { + fprintf(stderr, "could not allocate memory to parse routing key\n"); + exit(1); + } + + for (routing_key_token = + strtok_r(routing_tmp, LISTEN_KEYS_DELIMITER, &routing_key_rest); + NULL != routing_key_token && routing_key_count < MAX_LISTEN_KEYS - 1; + routing_key_token = + strtok_r(NULL, LISTEN_KEYS_DELIMITER, &routing_key_rest)) { + + if (!amqp_queue_bind(conn, 1, queue_bytes, eb, + cstring_bytes(routing_key_token), + amqp_empty_table)) { + die_rpc(amqp_get_rpc_reply(conn), "queue.bind"); + } + } + free(routing_tmp); + } + } + + return queue_bytes; +} + +#define AMQP_CONSUME_MAX_PREFETCH_COUNT 65535 + +static void do_consume(amqp_connection_state_t conn, amqp_bytes_t queue, + int no_ack, int count, int prefetch_count, + const char *const *argv) { + int i; + + /* If there is a limit, set the qos to match */ + if (count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT && + !amqp_basic_qos(conn, 1, 0, count, 0)) { + die_rpc(amqp_get_rpc_reply(conn), "basic.qos"); + } + + /* if there is a maximum number of messages to be received at a time, set the + * qos to match */ + if (prefetch_count > 0 && prefetch_count <= AMQP_CONSUME_MAX_PREFETCH_COUNT) { + /* the maximum number of messages to be received at a time must be less + * than the global maximum number of messages. */ + if (!(count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT && + prefetch_count >= count)) { + if (!amqp_basic_qos(conn, 1, 0, prefetch_count, 0)) { + die_rpc(amqp_get_rpc_reply(conn), "basic.qos"); + } + } + } + + if (!amqp_basic_consume(conn, 1, queue, amqp_empty_bytes, 0, no_ack, 0, + amqp_empty_table)) { + die_rpc(amqp_get_rpc_reply(conn), "basic.consume"); + } + + for (i = 0; count < 0 || i < count; i++) { + amqp_frame_t frame; + struct pipeline pl; + uint64_t delivery_tag; + amqp_basic_deliver_t *deliver; + int res = amqp_simple_wait_frame(conn, &frame); + die_amqp_error(res, "waiting for header frame"); + + if (frame.frame_type != AMQP_FRAME_METHOD || + frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) { + continue; + } + + deliver = (amqp_basic_deliver_t *)frame.payload.method.decoded; + delivery_tag = deliver->delivery_tag; + + pipeline(argv, &pl); + copy_body(conn, pl.infd); + + if (finish_pipeline(&pl) && !no_ack) + die_amqp_error(amqp_basic_ack(conn, 1, delivery_tag, 0), "basic.ack"); + + amqp_maybe_release_buffers(conn); + } +} + +int main(int argc, const char **argv) { + poptContext opts; + amqp_connection_state_t conn; + const char *const *cmd_argv; + static char *queue = NULL; + static char *exchange = NULL; + static char *routing_key = NULL; + static int declare = 0; + static int exclusive = 0; + static int no_ack = 0; + static int count = -1; + static int prefetch_count = -1; + amqp_bytes_t queue_bytes; + + struct poptOption options[] = { + INCLUDE_OPTIONS(connect_options), + {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue to consume from", + "queue"}, + {"exchange", 'e', POPT_ARG_STRING, &exchange, 0, + "bind the queue to this exchange", "exchange"}, + {"routing-key", 'r', POPT_ARG_STRING, &routing_key, 0, + "the routing key to bind with", "routing key"}, + {"declare", 'd', POPT_ARG_NONE, &declare, 0, + "declare an exclusive queue (deprecated, use --exclusive instead)", + NULL}, + {"exclusive", 'x', POPT_ARG_NONE, &exclusive, 0, + "declare the queue as exclusive", NULL}, + {"no-ack", 'A', POPT_ARG_NONE, &no_ack, 0, "consume in no-ack mode", + NULL}, + {"count", 'c', POPT_ARG_INT, &count, 0, + "stop consuming after this many messages are consumed", "limit"}, + {"prefetch-count", 'p', POPT_ARG_INT, &prefetch_count, 0, + "receive only this many message at a time from the server", "limit"}, + POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}}; + + opts = process_options(argc, argv, options, "[OPTIONS]... "); + + cmd_argv = poptGetArgs(opts); + if (!cmd_argv || !cmd_argv[0]) { + fprintf(stderr, "consuming command not specified\n"); + poptPrintUsage(opts, stderr, 0); + goto error; + } + + conn = make_connection(); + queue_bytes = + setup_queue(conn, queue, exchange, routing_key, declare, exclusive); + do_consume(conn, queue_bytes, no_ack, count, prefetch_count, cmd_argv); + close_connection(conn); + return 0; + +error: + poptFreeContext(opts); + return 1; +} diff --git a/ext/librabbitmq/tools/declare_queue.c b/ext/librabbitmq/tools/declare_queue.c new file mode 100644 index 000000000..0b9858054 --- /dev/null +++ b/ext/librabbitmq/tools/declare_queue.c @@ -0,0 +1,79 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "common.h" + +int main(int argc, const char **argv) { + amqp_connection_state_t conn; + static char *queue = NULL; + static int durable = 0; + + struct poptOption options[] = { + INCLUDE_OPTIONS(connect_options), + {"queue", 'q', POPT_ARG_STRING, &queue, 0, + "the queue name to declare, or the empty string", "queue"}, + {"durable", 'd', POPT_ARG_VAL, &durable, 1, "declare a durable queue", + NULL}, + POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}}; + + process_all_options(argc, argv, options); + + if (queue == NULL) { + fprintf(stderr, "queue name not specified\n"); + return 1; + } + + conn = make_connection(); + { + amqp_queue_declare_ok_t *reply = amqp_queue_declare( + conn, 1, cstring_bytes(queue), 0, durable, 0, 0, amqp_empty_table); + if (reply == NULL) { + die_rpc(amqp_get_rpc_reply(conn), "queue.declare"); + } + + printf("%.*s\n", (int)reply->queue.len, (char *)reply->queue.bytes); + } + close_connection(conn); + return 0; +} diff --git a/ext/librabbitmq/tools/delete_queue.c b/ext/librabbitmq/tools/delete_queue.c new file mode 100644 index 000000000..f9d01ab11 --- /dev/null +++ b/ext/librabbitmq/tools/delete_queue.c @@ -0,0 +1,81 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "common.h" + +int main(int argc, const char **argv) { + amqp_connection_state_t conn; + static char *queue = NULL; + static int if_unused = 0; + static int if_empty = 0; + + struct poptOption options[] = { + INCLUDE_OPTIONS(connect_options), + {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue name to delete", + "queue"}, + {"if-unused", 'u', POPT_ARG_VAL, &if_unused, 1, + "do not delete unless queue is unused", NULL}, + {"if-empty", 'e', POPT_ARG_VAL, &if_empty, 1, + "do not delete unless queue is empty", NULL}, + POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}}; + + process_all_options(argc, argv, options); + + if (queue == NULL || *queue == '\0') { + fprintf(stderr, "queue name not specified\n"); + return 1; + } + + conn = make_connection(); + { + amqp_queue_delete_ok_t *reply = + amqp_queue_delete(conn, 1, cstring_bytes(queue), if_unused, if_empty); + if (reply == NULL) { + die_rpc(amqp_get_rpc_reply(conn), "queue.delete"); + } + printf("%u\n", reply->message_count); + } + close_connection(conn); + return 0; +} diff --git a/ext/librabbitmq/tools/doc/amqp-consume.xml b/ext/librabbitmq/tools/doc/amqp-consume.xml new file mode 100644 index 000000000..9ee12e894 --- /dev/null +++ b/ext/librabbitmq/tools/doc/amqp-consume.xml @@ -0,0 +1,223 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + amqp-consume + 1 + RabbitMQ C Client + + + + amqp-consume + Consume messages from a queue on an AMQP server + + + + + amqp-consume + + OPTION + + + command + + + args + + + + + + Description + + amqp-consume consumes messages from a + queue on an AMQP server. For each message that arrives, a + receiving command is run, with the message body supplied + to it on standard input. + + + amqp-consume can consume from an + existing queue, or it can create a new queue. It can + optionally bind the queue to an existing exchange. + + + By default, messages will be consumed with explicit + acknowledgements. A message will only be acknowledged if + the receiving command exits successfully (i.e. with an + exit code of zero). The AMQP no ack mode + (a.k.a. auto-ack mode) can be enable with the + option. + + + + + Options + + + + =queue name + + + The name of the queue to consume messages + from. + + + + If the option is + omitted, the AMQP server will assign a unique + name to the queue, and that server-assigned + name will be dixsplayed on stderr; this case + implies that an exclusive queue should be + declared. + + + + + + =exchange name + + + Specifies that an exclusive queue should + be declared, and bound to the given exchange. + The specified exchange should already exist + unless the + option is used to request the creation of an + exchange. + + + + + + =routing key + + + The routing key for binding. If omitted, an + empty routing key is assumed. + + + + + + + + + Forces an exclusive queue to be declared, + even when it otherwise would not be. That is, + when a queue name is specified with the + option, but no + binding to an exchange is requested with the + option. + Note: this option is deprecated and may be + removed in a future version, use the + option to + explicitly declare an exclusive queue. + + + + + + + + + Declared queues are non-exclusive by default, + this option forces declaration of exclusive + queues. + + + + + + =routing key + + + Enable no ack mode: The AMQP + server will unconditionally acknowledge each + message that is delivered, regardless of + whether the target command exits successfully + or not. + + + + + + =limit + + + Stop consuming after the given number of + messages have been received. + + + + + + =limit + + + Request the server to only send + limit + messages at a time. + + + If any value was passed to , + the value passed to + should be smaller than that, or otherwise it will be + ignored. + + + If / is + passed, this option has no effect. + + + + + + + + Examples + + + Consume messages from an existing queue + myqueue, and + output the message bodies on standard output via + cat: + + $ amqp-publish -q myqueue cat + + + + + Bind a new exclusive queue to an + exchange myexch, and send + each message body to the script + myscript, automatically + acknowledging them on the server: + + $ amqp-consume -A -e myexch ./myscript + + + + + + + See also + + librabbitmq-tools7 + describes connection-related options common to all the + RabbitMQ C Client tools. + + + diff --git a/ext/librabbitmq/tools/doc/amqp-declare-queue.xml b/ext/librabbitmq/tools/doc/amqp-declare-queue.xml new file mode 100644 index 000000000..0fc04407b --- /dev/null +++ b/ext/librabbitmq/tools/doc/amqp-declare-queue.xml @@ -0,0 +1,122 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + amqp-declare-queue + 1 + RabbitMQ C Client + + + + amqp-declare-queue + Declare (create or assert the existence of) a queue on an AMQP server + + + + + amqp-declare-queue + + OPTION + + -d + -q queue name + + + + + Description + + amqp-declare-queue attempts to create a + queue on an AMQP server, and exits. If the empty-string is + supplied as the queue name, a fresh queue name is + generated by the server and returned. In all cases, if a + queue was successfully declared, the (raw binary) name of + the queue is printed to standard output, followed by a + newline. + + + + + Options + + + + =queue name + + + The name of the queue to declare. If the + empty string is supplied, a fresh queue name + is generated by the server. + + + + + + + + + Causes the queue to be declared with the + "durable" flag set. Durable queues survive + server restarts. By default, queues are declared + in "transient" mode. + + + + + + + + Exit Status + + If the queue was successfully declared, the exit status is + 0. If an error occurs, the exit status is 1. + + + + + Examples + + + Declare the durable queue myqueue, and + display the name of the queue on standard output: + + $ amqp-declare-queue -d -q myqueue +myqueue + + + + Declare a fresh, server-named transient queue, + and display the name of the queue on standard output + (use amqp-delete-queue + 1 to delete + it from the server once you're done): + + $ amqp-declare-queue -q "" +amq.gen-BW/wvociA8g6LFpb1PlqOA== + + + + + + + See also + + librabbitmq-tools7 + describes connection-related options common to all the + RabbitMQ C Client tools. + + + diff --git a/ext/librabbitmq/tools/doc/amqp-delete-queue.xml b/ext/librabbitmq/tools/doc/amqp-delete-queue.xml new file mode 100644 index 000000000..040a384de --- /dev/null +++ b/ext/librabbitmq/tools/doc/amqp-delete-queue.xml @@ -0,0 +1,94 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + amqp-delete-queue + 1 + RabbitMQ C Client + + + + amqp-delete-queue + Delete a queue from an AMQP server + + + + + amqp-delete-queue + + OPTION + + -q queue name + + + + + Description + + amqp-delete-queue deletes a queue from + an AMQP server, and exits after printing to standard + output the number of messages that were in the queue at + the time of its deletion. + + + + + Options + + + + =queue name + + + The name of the queue to delete. + + + + + + + + Exit Status + + If the queue was successfully deleted, the exit status is + 0. If an error occurs, the exit status is 1. + + + + + Examples + + + Delete the + queue myqueue + at a moment when it has 123 messages waiting on + it: + + $ amqp-delete-queue -q myqueue +123 + + + + + + + See also + + librabbitmq-tools7 + describes connection-related options common to all the + RabbitMQ C Client tools. + + + diff --git a/ext/librabbitmq/tools/doc/amqp-get.xml b/ext/librabbitmq/tools/doc/amqp-get.xml new file mode 100644 index 000000000..08abe2bdd --- /dev/null +++ b/ext/librabbitmq/tools/doc/amqp-get.xml @@ -0,0 +1,95 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + amqp-get + 1 + RabbitMQ C Client + + + + amqp-get + Get a message from a queue on an AMQP server + + + + + amqp-get + + OPTION + + -q queue name + + + + + Description + + amqp-get attempts to consume a single + message from a queue on an AMQP server, and exits. Unless + the queue was empty, the body of the resulting message is + sent to standard output. + + + + + Options + + + + =queue name + + + The name of the queue to consume messages + from. + + + + + + + + Exit Status + + If the queue is not empty, and a message is successfully + retrieved, the exit status is 0. If an error occurs, the + exit status is 1. If the queue is found to be empty, the + exit status is 2. + + + + + Examples + + + Get a message from the queue myqueue, and + display its body on standard output: + + $ amqp-get -q myqueue + + + + + + + See also + + librabbitmq-tools7 + describes connection-related options common to all the + RabbitMQ C Client tools. + + + diff --git a/ext/librabbitmq/tools/doc/amqp-publish.xml b/ext/librabbitmq/tools/doc/amqp-publish.xml new file mode 100644 index 000000000..aae07f47a --- /dev/null +++ b/ext/librabbitmq/tools/doc/amqp-publish.xml @@ -0,0 +1,169 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + amqp-publish + 1 + RabbitMQ C Client + + + + amqp-publish + Publish a message on an AMQP server + + + + + amqp-publish + + OPTION + + + + + + Description + + Publishes a message to an exchange on an AMQP server. + Options allow the various properties of the message and + parameters of the AMQP basic.publish + method to be specified. + + + By default, the message body is read from standard input. + Alternatively, the option allows the message + body to be provided as part of the command. + + + + + Options + + + + =exchange name + + + The name of the exchange to publish to. If + omitted, the default exchange (also known as + the nameless exchange) is used. + + + + + + =routing key + + + The routing key to publish with. If omitted, + an empty routing key is assumed. A routing + key must be specified when publishing to the + default exchange; in that case, accoding to + the AMQP specification, the routing key + corresponds to a queue name. + + + + + + + + + Use the persistent delivery mode. Without + this option, non-persistent delivery is used. + + + + + + =MIME type + + + Specifies the content-type property for the + message. If omitted, the content-type + property is not set on the message. + + + + + + =content coding + + + Specifies the content-encoding property for + the message. If omitted, the content-encoding + property is not set on the message. + + + + + + =message body + + + Specifies the message body. If omitted, the + message body is read from standard input. + + + + + + =header + + + Specifies an optional header in the form "key: value". + + + + + + + + Examples + + + Send a short message, consisting of the word + Hello to the queue + myqueue via the + default exchange: + + $ amqp-publish -r myqueue -b Hello + + + + + Send some XML data from a file to the exchange + events, with + persistent delivery mode, setting the content-type + property on the message to make the data format + explicit: + + $ amqp-publish -e events -p -C text/xml <event.xml + + + + + + + See also + + librabbitmq-tools7 + describes connection-related options common to all the + RabbitMQ C Client tools. + + + diff --git a/ext/librabbitmq/tools/doc/librabbitmq-tools.xml b/ext/librabbitmq/tools/doc/librabbitmq-tools.xml new file mode 100644 index 000000000..e7e98aa7b --- /dev/null +++ b/ext/librabbitmq/tools/doc/librabbitmq-tools.xml @@ -0,0 +1,90 @@ + + +] +> + + + RabbitMQ C Client + + The RabbitMQ Team <info@rabbitmq.com> + + &date; + + + + librabbitmq-tools + 7 + RabbitMQ C Client + + + + librabbitmq-tools + Command line AMQP tools + + + + Description + + A set of command line AMQP tools based on librabbitmq. This page + describes common options and conventions used by all of + the tools. + + + + + Common Options + + + + =hostname:port + + + The host name (or address) to connect to. + Defaults to localhost. The port number may + also be specified; if omitted, it defaults to + the standard AMQP port number (5672). + + + + + =vhost + + + The AMQP vhost to specify when connecting. + Defaults to /. + + + + + =username + + + The username to authenticate to the AMQP server with. Defaults to guest. + + + + + =password + + + The password to authenticate to the AMQP server with. Defaults to guest. + + + + + + + + See also + + + amqp-publish1 + amqp-consume1 + amqp-get1 + + + + diff --git a/ext/librabbitmq/tools/get.c b/ext/librabbitmq/tools/get.c new file mode 100644 index 000000000..f418e2fde --- /dev/null +++ b/ext/librabbitmq/tools/get.c @@ -0,0 +1,78 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "common.h" + +static int do_get(amqp_connection_state_t conn, char *queue) { + amqp_rpc_reply_t r = amqp_basic_get(conn, 1, cstring_bytes(queue), 1); + die_rpc(r, "basic.get"); + + if (r.reply.id == AMQP_BASIC_GET_EMPTY_METHOD) { + return 0; + } + + copy_body(conn, 1); + return 1; +} + +int main(int argc, const char **argv) { + amqp_connection_state_t conn; + static char *queue = NULL; + int got_something; + + struct poptOption options[] = { + INCLUDE_OPTIONS(connect_options), + {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue to consume from", + "queue"}, + POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}}; + + process_all_options(argc, argv, options); + + if (!queue) { + fprintf(stderr, "queue not specified\n"); + return 1; + } + + conn = make_connection(); + got_something = do_get(conn, queue); + close_connection(conn); + return got_something ? 0 : 2; +} diff --git a/ext/librabbitmq/tools/publish.c b/ext/librabbitmq/tools/publish.c new file mode 100644 index 000000000..b2a2a1ef3 --- /dev/null +++ b/ext/librabbitmq/tools/publish.c @@ -0,0 +1,180 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "common.h" + +#define MAX_LINE_LENGTH 1024 * 32 + +static void do_publish(amqp_connection_state_t conn, char *exchange, + char *routing_key, amqp_basic_properties_t *props, + amqp_bytes_t body) { + int res = amqp_basic_publish(conn, 1, cstring_bytes(exchange), + cstring_bytes(routing_key), 0, 0, props, body); + die_amqp_error(res, "basic.publish"); +} + +int main(int argc, const char **argv) { + amqp_connection_state_t conn; + static char *exchange = NULL; + static char *routing_key = NULL; + static char *content_type = NULL; + static char *content_encoding = NULL; + static char **headers = NULL; + static char *reply_to = NULL; + static char *body = NULL; + amqp_basic_properties_t props; + amqp_bytes_t body_bytes; + static int delivery = 1; /* non-persistent by default */ + static int line_buffered = 0; + static char **pos; + + struct poptOption options[] = { + INCLUDE_OPTIONS(connect_options), + {"exchange", 'e', POPT_ARG_STRING, &exchange, 0, + "the exchange to publish to", "exchange"}, + {"routing-key", 'r', POPT_ARG_STRING, &routing_key, 0, + "the routing key to publish with", "routing key"}, + {"persistent", 'p', POPT_ARG_VAL, &delivery, 2, + "use the persistent delivery mode", NULL}, + {"content-type", 'C', POPT_ARG_STRING, &content_type, 0, + "the content-type for the message", "content type"}, + {"reply-to", 't', POPT_ARG_STRING, &reply_to, 0, + "the replyTo to use for the message", "reply to"}, + {"line-buffered", 'l', POPT_ARG_VAL, &line_buffered, 2, + "treat each line from standard in as a separate message", NULL}, + {"content-encoding", 'E', POPT_ARG_STRING, &content_encoding, 0, + "the content-encoding for the message", "content encoding"}, + {"header", 'H', POPT_ARG_ARGV, &headers, 0, + "set a message header (may be specified multiple times)", + "\"key: value\""}, + {"body", 'b', POPT_ARG_STRING, &body, 0, "specify the message body", + "body"}, + POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}}; + + process_all_options(argc, argv, options); + + if (!exchange && !routing_key) { + fprintf(stderr, "neither exchange nor routing key specified\n"); + return 1; + } + + memset(&props, 0, sizeof props); + props._flags = AMQP_BASIC_DELIVERY_MODE_FLAG; + props.delivery_mode = delivery; + + if (content_type) { + props._flags |= AMQP_BASIC_CONTENT_TYPE_FLAG; + props.content_type = amqp_cstring_bytes(content_type); + } + + if (content_encoding) { + props._flags |= AMQP_BASIC_CONTENT_ENCODING_FLAG; + props.content_encoding = amqp_cstring_bytes(content_encoding); + } + + if (reply_to) { + props._flags |= AMQP_BASIC_REPLY_TO_FLAG; + props.reply_to = amqp_cstring_bytes(reply_to); + } + + if (headers) { + int num = 0; + for (pos = headers; *pos; pos++) { + num++; + } + + if (num > 0) { + amqp_table_t *table = &props.headers; + table->num_entries = num; + table->entries = calloc(num, sizeof(amqp_table_entry_t)); + int i = 0; + for (pos = headers; *pos; pos++) { + char *colon = strchr(*pos, ':'); + if (colon) { + *colon++ = '\0'; + while (*colon == ' ') colon++; + table->entries[i].key = amqp_cstring_bytes(*pos); + table->entries[i].value.kind = AMQP_FIELD_KIND_UTF8; + table->entries[i].value.value.bytes = amqp_cstring_bytes(colon); + i++; + } else { + fprintf(stderr, + "Ignored header definition missing ':' delimiter in \"%s\"\n", + *pos); + } + } + props._flags |= AMQP_BASIC_HEADERS_FLAG; + } + } + + conn = make_connection(); + + if (body) { + body_bytes = amqp_cstring_bytes(body); + } else { + if (line_buffered) { + body_bytes.bytes = (char *)malloc(MAX_LINE_LENGTH); + while (fgets(body_bytes.bytes, MAX_LINE_LENGTH, stdin)) { + body_bytes.len = strlen(body_bytes.bytes); + do_publish(conn, exchange, routing_key, &props, body_bytes); + } + } else { + body_bytes = read_all(0); + } + } + + if (!line_buffered) { + do_publish(conn, exchange, routing_key, &props, body_bytes); + } + + if (props.headers.num_entries > 0) { + free(props.headers.entries); + } + + if (!body) { + free(body_bytes.bytes); + } + + close_connection(conn); + return 0; +} diff --git a/ext/librabbitmq/tools/unix/process.c b/ext/librabbitmq/tools/unix/process.c new file mode 100644 index 000000000..596f40a88 --- /dev/null +++ b/ext/librabbitmq/tools/unix/process.c @@ -0,0 +1,91 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "common.h" +#include "process.h" + +extern char **environ; + +void pipeline(const char *const *argv, struct pipeline *pl) { + posix_spawn_file_actions_t file_acts; + + int pipefds[2]; + if (pipe(pipefds)) { + die_errno(errno, "pipe"); + } + + die_errno(posix_spawn_file_actions_init(&file_acts), + "posix_spawn_file_actions_init"); + die_errno(posix_spawn_file_actions_adddup2(&file_acts, pipefds[0], 0), + "posix_spawn_file_actions_adddup2"); + die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[0]), + "posix_spawn_file_actions_addclose"); + die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[1]), + "posix_spawn_file_actions_addclose"); + + die_errno(posix_spawnp(&pl->pid, argv[0], &file_acts, NULL, + (char *const *)argv, environ), + "posix_spawnp: %s", argv[0]); + + die_errno(posix_spawn_file_actions_destroy(&file_acts), + "posix_spawn_file_actions_destroy"); + + if (close(pipefds[0])) { + die_errno(errno, "close"); + } + + pl->infd = pipefds[1]; +} + +int finish_pipeline(struct pipeline *pl) { + int status; + + if (close(pl->infd)) { + die_errno(errno, "close"); + } + if (waitpid(pl->pid, &status, 0) < 0) { + die_errno(errno, "waitpid"); + } + return WIFEXITED(status) && WEXITSTATUS(status) == 0; +} diff --git a/ext/librabbitmq/tools/unix/process.h b/ext/librabbitmq/tools/unix/process.h new file mode 100644 index 000000000..59673f2c4 --- /dev/null +++ b/ext/librabbitmq/tools/unix/process.h @@ -0,0 +1,42 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +struct pipeline { + int pid; + int infd; +}; + +extern void pipeline(const char *const *argv, struct pipeline *pl); +extern int finish_pipeline(struct pipeline *pl); diff --git a/ext/librabbitmq/tools/win32/compat.c b/ext/librabbitmq/tools/win32/compat.c new file mode 100644 index 000000000..10828a6db --- /dev/null +++ b/ext/librabbitmq/tools/win32/compat.c @@ -0,0 +1,65 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "compat.h" + +int asprintf(char **strp, const char *fmt, ...) { + va_list ap; + int len; + + va_start(ap, fmt); + len = _vscprintf(fmt, ap); + va_end(ap); + + *strp = malloc(len + 1); + if (!*strp) { + return -1; + } + + va_start(ap, fmt); + _vsnprintf(*strp, len + 1, fmt, ap); + va_end(ap); + + (*strp)[len] = 0; + return len; +} diff --git a/ext/librabbitmq/tools/win32/compat.h b/ext/librabbitmq/tools/win32/compat.h new file mode 100644 index 000000000..d08532be1 --- /dev/null +++ b/ext/librabbitmq/tools/win32/compat.h @@ -0,0 +1,36 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +extern int asprintf(char **strp, const char *fmt, ...); diff --git a/ext/librabbitmq/tools/win32/process.c b/ext/librabbitmq/tools/win32/process.c new file mode 100644 index 000000000..fbb68f04e --- /dev/null +++ b/ext/librabbitmq/tools/win32/process.c @@ -0,0 +1,227 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "common.h" +#include "process.h" + +void die_windows_error(const char *fmt, ...) { + char *msg; + + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (!FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, + GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&msg, 0, NULL)) { + msg = "(failed to retrieve Windows error message)"; + } + + fprintf(stderr, ": %s\n", msg); + exit(1); +} + +static char *make_command_line(const char *const *argv) { + int i; + size_t len = 1; /* initial quotes */ + char *buf; + char *dest; + + /* calculate the length of the required buffer, making worst + case assumptions for simplicity */ + for (i = 0;;) { + /* each character could need escaping */ + len += strlen(argv[i]) * 2; + + if (!argv[++i]) { + break; + } + + len += 3; /* quotes, space, quotes */ + } + + len += 2; /* final quotes and the terminating zero */ + + dest = buf = malloc(len); + if (!buf) { + die("allocating memory for subprocess command line"); + } + + /* Here we perform the inverse of the CommandLineToArgvW + function. Note that its rules are slightly crazy: A + sequence of backslashes only act to escape if followed by + double quotes. A sequence of backslashes not followed by + double quotes is untouched. */ + + for (i = 0;;) { + const char *src = argv[i]; + int backslashes = 0; + + *dest++ = '\"'; + + for (;;) { + switch (*src) { + case 0: + goto done; + + case '\"': + for (; backslashes; backslashes--) { + *dest++ = '\\'; + } + + *dest++ = '\\'; + *dest++ = '\"'; + break; + + case '\\': + backslashes++; + *dest++ = '\\'; + break; + + default: + backslashes = 0; + *dest++ = *src; + break; + } + + src++; + } + done: + for (; backslashes; backslashes--) { + *dest++ = '\\'; + } + + *dest++ = '\"'; + + if (!argv[++i]) { + break; + } + + *dest++ = ' '; + } + + *dest++ = 0; + return buf; +} + +void pipeline(const char *const *argv, struct pipeline *pl) { + HANDLE in_read_handle, in_write_handle; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION proc_info; + STARTUPINFO start_info; + char *cmdline = make_command_line(argv); + + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = TRUE; + sec_attr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&in_read_handle, &in_write_handle, &sec_attr, 0)) { + die_windows_error("CreatePipe"); + } + + if (!SetHandleInformation(in_write_handle, HANDLE_FLAG_INHERIT, 0)) { + die_windows_error("SetHandleInformation"); + } + + /* when in Rome... */ + ZeroMemory(&proc_info, sizeof proc_info); + ZeroMemory(&start_info, sizeof start_info); + + start_info.cb = sizeof start_info; + start_info.dwFlags |= STARTF_USESTDHANDLES; + + if ((start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE)) == + INVALID_HANDLE_VALUE || + (start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)) == + INVALID_HANDLE_VALUE) { + die_windows_error("GetStdHandle"); + } + + start_info.hStdInput = in_read_handle; + + if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, + &start_info, &proc_info)) { + die_windows_error("CreateProcess"); + } + + free(cmdline); + + if (!CloseHandle(proc_info.hThread)) { + die_windows_error("CloseHandle for thread"); + } + if (!CloseHandle(in_read_handle)) { + die_windows_error("CloseHandle"); + } + + pl->proc_handle = proc_info.hProcess; + pl->infd = _open_osfhandle((intptr_t)in_write_handle, 0); +} + +int finish_pipeline(struct pipeline *pl) { + DWORD code; + + if (close(pl->infd)) { + die_errno(errno, "close"); + } + + for (;;) { + if (!GetExitCodeProcess(pl->proc_handle, &code)) { + die_windows_error("GetExitCodeProcess"); + } + if (code != STILL_ACTIVE) { + break; + } + + if (WaitForSingleObject(pl->proc_handle, INFINITE) == WAIT_FAILED) { + die_windows_error("WaitForSingleObject"); + } + } + + if (!CloseHandle(pl->proc_handle)) { + die_windows_error("CloseHandle for process"); + } + + return code; +} diff --git a/ext/librabbitmq/tools/win32/process.h b/ext/librabbitmq/tools/win32/process.h new file mode 100644 index 000000000..0dd05fbf9 --- /dev/null +++ b/ext/librabbitmq/tools/win32/process.h @@ -0,0 +1,44 @@ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#include + +struct pipeline { + HANDLE proc_handle; + int infd; +}; + +extern void pipeline(const char *const *argv, struct pipeline *pl); +extern int finish_pipeline(struct pipeline *pl); diff --git a/ext/librabbitmq/travis.sh b/ext/librabbitmq/travis.sh new file mode 100644 index 000000000..c21993b02 --- /dev/null +++ b/ext/librabbitmq/travis.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +build_cmake() { + mkdir $PWD/_build && cd $PWD/_build + cmake .. -DCMAKE_INSTALL_PREFIX=$PWD/../_install -DCMAKE_C_FLAGS="-Werror" \ + ${_CMAKE_OPENSSL_FLAG} + cmake --build . --target install + ctest -V . +} + +build_format() { + ./travis/run-clang-format/run-clang-format.py \ + --clang-format-executable="${PWD}/travis/clang-format.sh" \ + --recursive examples librabbitmq tests tools +} + +build_coverage() { + mkdir $PWD/_build && cd $PWD/_build + cmake .. -DCMAKE_BUILD_TYPE=Coverage -DCMAKE_INSTALL_PREFIX=$PWD/../_install \ + -DCMAKE_C_FLAGS="-Werror -fprofile-arcs -ftest-coverage" \ + ${_CMAKE_OPENSSL_FLAG} + cmake --build . --target install + ctest -V . + + pip install --user cpp-coveralls + coveralls --exclude tests --build-root . --root .. --gcov-options '\-lp' +} + +build_asan() { + mkdir $PWD/_build && cd $PWD/_build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$PWD/../_install \ + -DCMAKE_C_FLAGS="-Werror -fsanitize=address,undefined -O1" + cmake --build . --target install + ctest -V . +} + +build_tsan() { + mkdir $PWD/_build && cd $PWD/_build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$PWD/../_install \ + -DCMAKE_C_FLAGS="-Werror -fsanitize=thread,undefined -O1" + cmake --build . --target install + ctest -V . +} + +build_scan-build() { + mkdir $PWD/_build && cd $PWD/_build + scan-build-3.9 cmake .. -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_PREFIX=$PWD/../_install \ + -DCMAKE_C_FLAGS="-Werror" + scan-build-3.9 make install +} + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 {cmake|asan|tsan|scan-build}" + exit 1 +fi + +set -e # exit on error. +set -x # echo commands. + +case $TRAVIS_OS_NAME in +osx) + # This prints out a long list of updated packages, which isn't useful. + brew update > /dev/null + brew install popt + brew outdated openssl || brew install openssl + export _CMAKE_OPENSSL_FLAG="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl" + ;; +esac + +eval "build_$1" diff --git a/ext/librabbitmq/travis/clang-format.sh b/ext/librabbitmq/travis/clang-format.sh new file mode 100644 index 000000000..3b48c3be4 --- /dev/null +++ b/ext/librabbitmq/travis/clang-format.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +exec clang-format-3.9 -style=file $@ diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 6edeb8a15..2da12598f 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -46,28 +46,7 @@ #include #endif /* Windows or not */ -#if defined (_MSC_VER) -#ifdef ZT_EXPORT -#define ZT_SDK_API __declspec(dllexport) -#else -#define ZT_SDK_API __declspec(dllimport) -#if !defined(ZT_SDK) -#ifdef _DEBUG -#ifdef _WIN64 -#pragma comment(lib, "ZeroTierOne_x64d.lib") -#else -#pragma comment(lib, "ZeroTierOne_x86d.lib") -#endif -#else -#ifdef _WIN64 -#pragma comment(lib, "ZeroTierOne_x64.lib") -#else -#pragma comment(lib, "ZeroTierOne_x86.lib") -#endif -#endif -#endif -#endif -#else +#ifndef ZT_SDK_API #define ZT_SDK_API #endif diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt new file mode 100644 index 000000000..add93405b --- /dev/null +++ b/node/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required (VERSION 2.8) +project(zt_core) + +if(WIN32) + add_definitions(-DNOMINMAX) +endif(WIN32) + +set(core_headers + Address.hpp + AES.hpp + AtomicCounter.hpp + Buffer.hpp + C25519.hpp + Capability.hpp + CertificateOfMembership.hpp + CertificateOfOwnership.hpp + Constants.hpp + Credential.hpp + Dictionary.hpp + ECC384.hpp + Hashtable.hpp + Identity.hpp + InetAddress.hpp + Locator.hpp + MAC.hpp + Membership.hpp + Multicaster.hpp + MulticastGroup.hpp + Mutex.hpp + Network.hpp + NetworkConfig.hpp + Node.hpp + OutboundMulticast.hpp + Packet.hpp + Path.hpp + Peer.hpp + Poly1305.hpp + RingBuffer.hpp + RuntimeEnvironment.hpp + Salsa20.hpp + SelfAwareness.hpp + SHA512.hpp + SharedPtr.hpp + Str.hpp + Switch.hpp + Tag.hpp + Topology.hpp + Trace.hpp + Utils.hpp +) +set(core_src + AES.cpp + C25519.cpp + Capability.cpp + CertificateOfMembership.cpp + CertificateOfOwnership.cpp + ECC384.cpp + Identity.cpp + IncomingPacket.cpp + InetAddress.cpp + Membership.cpp + Multicaster.cpp + Network.cpp + NetworkConfig.cpp + Node.cpp + OutboundMulticast.cpp + Packet.cpp + Path.cpp + Peer.cpp + Poly1305.cpp + Revocation.cpp + Salsa20.cpp + SelfAwareness.cpp + SHA512.cpp + Switch.cpp + Tag.cpp + Topology.cpp + Trace.cpp + Utils.cpp +) +add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) + +if(UNIX) + set_source_files_properties( + AES.cpp + ECC384.cpp + Salsa20.cpp + SHA512.cpp + C25519.cpp + Poly1305.cpp + PROPERTIES + COMPILE_FLAGS "-Wall -O3" + ) +endif(UNIX) diff --git a/osdep/CMakeLists.txt b/osdep/CMakeLists.txt new file mode 100644 index 000000000..f9e5d6d40 --- /dev/null +++ b/osdep/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 2.8) +project(zt_osdep) + +set(src + Arp.cpp + Http.cpp + EthernetTap.cpp + ManagedRoute.cpp + NeighborDiscovery.cpp + OSUtils.cpp + PortMapper.cpp +) + +set(headers + Arp.hpp + Binder.hpp + BlockingQueue.hpp + EthernetTap.hpp + Http.hpp + ManagedRoute.hpp + OSUtils.hpp + Phy.hpp + PortMapper.hpp + Thread.hpp +) + +if(WIN32) + set(src ${src} WindowsEthernetTap.cpp) + set(headers ${headers} WindowsEthernetTap.hpp) +elseif(UNIX) + if(APPLE) + set(src ${src} MacEthernetTap.cpp) + set(headers ${headers} MacEthernetTap.hpp) + endif(APPLE) + + if(CMAKE_SYSTEM_NAME MATCHES "BSD") + set(src ${src} BSDEthernetTap.cpp) + set(headers ${headers} BSDEthernetTap.hpp) + endif() + + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(src ${src} freebsd_getifmaddrs.c) + set(headers ${headers} freebsd_getifmaddrs.h) + endif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(src ${src} LinuxEthernetTap.cpp LinuxNetLink.cpp) + set(headers ${headers} LinuxEthernetTap.hpp LinuxNetLink.hpp) + endif(CMAKE_SYSTEM_NAME MATCHES "Linux") +endif(WIN32) + +add_library(${PROJECT_NAME} STATIC ${src} ${headers}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) + +if(APPLE) + add_executable(MacEthernetTapAgent MacEthernetTapAgent.c MacEthernetTapAgent.h) +endif(APPLE) + + diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt new file mode 100644 index 000000000..cd1ec2e6e --- /dev/null +++ b/service/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) +project(zt_service) + +if(WIN32) + add_definitions(-DNOMINMAX) +endif(WIN32) + +set(src + OneService.cpp + SoftwareUpdater.cpp +) + +set(headers + OneService.hpp + SoftwareUpdater.hpp +) + +add_library(${PROJECT_NAME} STATIC ${src} ${headers}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) + diff --git a/version.h b/version.h.in similarity index 85% rename from version.h rename to version.h.in index 106787935..99c3ba4d0 100644 --- a/version.h +++ b/version.h.in @@ -30,17 +30,17 @@ /** * Major version */ -#define ZEROTIER_ONE_VERSION_MAJOR 1 +#define ZEROTIER_ONE_VERSION_MAJOR @ZEROTIER_ONE_VERSION_MAJOR@ /** * Minor version */ -#define ZEROTIER_ONE_VERSION_MINOR 4 +#define ZEROTIER_ONE_VERSION_MINOR @ZEROTIER_ONE_VERSION_MINOR@ /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 2 +#define ZEROTIER_ONE_VERSION_REVISION @ZEROTIER_ONE_VERSION_REVISION@ /** * Build version @@ -49,7 +49,7 @@ * to force a minor update without an actual version number change. It's * not part of the actual release version number. */ -#define ZEROTIER_ONE_VERSION_BUILD 0 +#define ZEROTIER_ONE_VERSION_BUILD @ZEROTIER_ONE_VERSION_BUILD@ #ifndef ZT_BUILD_ARCHITECTURE #define ZT_BUILD_ARCHITECTURE 0 diff --git a/windows/WinUI/App.config b/windows/WinUI/App.config index 8e1564635..88fa4027b 100644 --- a/windows/WinUI/App.config +++ b/windows/WinUI/App.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/windows/WinUI/CMakeLists.txt b/windows/WinUI/CMakeLists.txt new file mode 100644 index 000000000..54cbbc6e1 --- /dev/null +++ b/windows/WinUI/CMakeLists.txt @@ -0,0 +1,128 @@ +cmake_minimum_required(VERSION 3.8) +include(CSharpUtilities) + +project("ZeroTierOneUI" VERSION 1.4.0 LANGUAGES CSharp) + +add_executable( + ${PROJECT_NAME} + app.manifest + App.config + App.xaml + App.xaml.cs + + AboutView.xaml + AboutView.xaml.cs + APIHandler.cs + CentralAPI.cs + CentralLogin.cs + CentralNetwork.cs + CentralServer.cs + CentralToken.cs + CentralUser.cs + ISwitchable.cs + JoinNetworkView.xaml + JoinNetworkView.xaml.cs + NetworkInfoView.xaml + NetworkInfoView.xaml.cs + NetworkListView.xaml + NetworkListView.xaml.cs + NetworkMonitor.cs + NetworkNameGenerator.cs + NetworkRoute.cs + NetworksPage.xaml + NetworksPage.xaml.cs + PeersPage.xaml + PeersPage.xaml.cs + PreferencesView.xaml + PreferencesView.xaml.cs + "Simple Styles.xaml" + ToolbarItem.xaml + ToolbarItem.xaml.cs + ZeroTierNetwork.cs + ZeroTierPeer.cs + ZeroTierPeerPhysicalPath.cs + ZeroTierStatus.cs + + packages.config + + "Properties/AssemblyInfo.cs" + "Properties/Resources.Designer.cs" + "Properties/Resources.resx" + "Properties/Settings.Designer.cs" + "Properties/Settings.settings" + + "Resources/ZeroTierIcon.ico" + ZeroTierIcon.ico +) + +csharp_set_designer_cs_properties( + "Properties/AssemblyInfo.cs" + "Properties/Resources.Designer.cs" + "Properties/Resources.resx" + "Properties/Settings.Designer.cs" + "Properties/Settings.settings" +) + +csharp_set_xaml_cs_properties( + App.xaml + App.xaml.cs + AboutView.xaml + AboutView.xaml.cs + JoinNetworkView.xaml + JoinNetworkView.xaml.cs + NetworkInfoView.xaml + NetworkInfoView.xaml.cs + NetworkListView.xaml + NetworkListView.xaml.cs + NetworksPage.xaml + NetworksPage.xaml.cs + PeersPage.xaml + PeersPage.xaml.cs + PreferencesView.xaml + PreferencesView.xaml.cs + ToolbarItem.xaml + ToolbarItem.xaml.cs +) + +set_property(SOURCE App.xaml PROPERTY VS_XAML_TYPE "ApplicationDefinition") + +set_property(SOURCE ZeroTierIcon.ico PROPERTY VS_TOOL_OVERRIDE "Resource") + +set_target_properties(${PROJECT_NAME} PROPERTIES + VS_GLOBAL_ROOTNAMESPACE "WinUI" + VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.5.2" + WIN32_EXECUTABLE TRUE + ) + +set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCES + "Microsoft.CSharp" + "PresentationCore" + "PresentationFramework" + "System" + "System.Core" + "System.Data" + "System.Data.DataSetExtensions" + "System.Drawing" + "System.Net.Http" + "System.Xaml" + "System.Xml" + "System.Xml.Linq" + "WindowsBase" + "Newtonsoft.Json" +) + +set(CMAKE_CSharp_FLAGS "/langversion:6") + +target_compile_options(${PROJECT_NAME} PRIVATE "/win32icon:${CMAKE_CURRENT_SOURCE_DIR}/ZeroTierIcon.ico") +set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCE_Hardcodet.Wpf.TaskbarNotification "${CMAKE_CURRENT_BINARY_DIR}/packages/Hardcodet.NotifyIcon.Wpf.1.0.8/lib/net45/Hardcodet.Wpf.TaskbarNotification.dll") + +find_program(NUGET nuget) +add_custom_target(nuget-restore + COMMAND ${NUGET} restore ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.sln +) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packages.config + ${CMAKE_CURRENT_BINARY_DIR}/packages.config COPYONLY) + +add_dependencies(${PROJECT_NAME} nuget-restore) + \ No newline at end of file diff --git a/windows/WinUI/NetworkMonitor.cs b/windows/WinUI/NetworkMonitor.cs index 927f804c4..b94c07852 100644 --- a/windows/WinUI/NetworkMonitor.cs +++ b/windows/WinUI/NetworkMonitor.cs @@ -58,7 +58,7 @@ namespace WinUI private void loadNetworks() { String dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One"; - String dataFile = Path.Combine(dataPath, "networks.dat"); + String dataFile = Path.Combine(dataPath, "networksv2.dat"); if (File.Exists(dataFile)) { diff --git a/windows/WinUI/Properties/AssemblyInfo.cs b/windows/WinUI/Properties/AssemblyInfo.cs index 9c7cd1339..a235ac285 100644 --- a/windows/WinUI/Properties/AssemblyInfo.cs +++ b/windows/WinUI/Properties/AssemblyInfo.cs @@ -12,7 +12,7 @@ using System.Windows; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("ZeroTier, Inc")] [assembly: AssemblyProduct("ZeroTier One")] -[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyCopyright("Copyright © 2015-2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/windows/WinUI/Properties/Resources.Designer.cs b/windows/WinUI/Properties/Resources.Designer.cs index 3e5c78aee..7e8b05965 100644 --- a/windows/WinUI/Properties/Resources.Designer.cs +++ b/windows/WinUI/Properties/Resources.Designer.cs @@ -19,10 +19,10 @@ namespace WinUI.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -36,7 +36,7 @@ namespace WinUI.Properties { /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinUI.Properties.Resources", typeof(Resources).Assembly); @@ -51,7 +51,7 @@ namespace WinUI.Properties { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -63,7 +63,17 @@ namespace WinUI.Properties { /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// - internal static System.Drawing.Icon ZeroTierIcon { + public static System.Drawing.Icon Icon1 { + get { + object obj = ResourceManager.GetObject("Icon1", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + public static System.Drawing.Icon ZeroTierIcon { get { object obj = ResourceManager.GetObject("ZeroTierIcon", resourceCulture); return ((System.Drawing.Icon)(obj)); diff --git a/windows/copyutil/CMakeLists.txt b/windows/copyutil/CMakeLists.txt new file mode 100644 index 000000000..f5e1215b5 --- /dev/null +++ b/windows/copyutil/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.8) +include(CSharpUtilities) + +project(copyutil VERSION 1.4.0 LANGUAGES CSharp) + +add_executable( + ${PROJECT_NAME} + App.config + Program.cs + + "Properties/AssemblyInfo.cs" +) \ No newline at end of file